mirror of
https://github.com/jackc/pgx.git
synced 2025-04-27 13:14:32 +00:00
This improves handling of unregistered types. In general, they should "just work". But there are performance benefits gained and some edge cases avoided by registering types. Updated documentation to mention this. https://github.com/jackc/pgx/issues/1445
2081 lines
69 KiB
Go
2081 lines
69 KiB
Go
package pgtype
|
|
|
|
import (
|
|
"database/sql"
|
|
"database/sql/driver"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/netip"
|
|
"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
|
|
JSONArrayOID = 199
|
|
PointOID = 600
|
|
LsegOID = 601
|
|
PathOID = 602
|
|
BoxOID = 603
|
|
PolygonOID = 604
|
|
LineOID = 628
|
|
LineArrayOID = 629
|
|
CIDROID = 650
|
|
CIDRArrayOID = 651
|
|
Float4OID = 700
|
|
Float8OID = 701
|
|
CircleOID = 718
|
|
CircleArrayOID = 719
|
|
UnknownOID = 705
|
|
MacaddrOID = 829
|
|
InetOID = 869
|
|
BoolArrayOID = 1000
|
|
QCharArrayOID = 1003
|
|
NameArrayOID = 1003
|
|
Int2ArrayOID = 1005
|
|
Int4ArrayOID = 1007
|
|
TextArrayOID = 1009
|
|
TIDArrayOID = 1010
|
|
ByteaArrayOID = 1001
|
|
XIDArrayOID = 1011
|
|
CIDArrayOID = 1012
|
|
BPCharArrayOID = 1014
|
|
VarcharArrayOID = 1015
|
|
Int8ArrayOID = 1016
|
|
PointArrayOID = 1017
|
|
LsegArrayOID = 1018
|
|
PathArrayOID = 1019
|
|
BoxArrayOID = 1020
|
|
Float4ArrayOID = 1021
|
|
Float8ArrayOID = 1022
|
|
PolygonArrayOID = 1027
|
|
OIDArrayOID = 1028
|
|
ACLItemOID = 1033
|
|
ACLItemArrayOID = 1034
|
|
MacaddrArrayOID = 1040
|
|
InetArrayOID = 1041
|
|
BPCharOID = 1042
|
|
VarcharOID = 1043
|
|
DateOID = 1082
|
|
TimeOID = 1083
|
|
TimestampOID = 1114
|
|
TimestampArrayOID = 1115
|
|
DateArrayOID = 1182
|
|
TimeArrayOID = 1183
|
|
TimestamptzOID = 1184
|
|
TimestamptzArrayOID = 1185
|
|
IntervalOID = 1186
|
|
IntervalArrayOID = 1187
|
|
NumericArrayOID = 1231
|
|
BitOID = 1560
|
|
BitArrayOID = 1561
|
|
VarbitOID = 1562
|
|
VarbitArrayOID = 1563
|
|
NumericOID = 1700
|
|
RecordOID = 2249
|
|
RecordArrayOID = 2287
|
|
UUIDOID = 2950
|
|
UUIDArrayOID = 2951
|
|
JSONBOID = 3802
|
|
JSONBArrayOID = 3807
|
|
DaterangeOID = 3912
|
|
DaterangeArrayOID = 3913
|
|
Int4rangeOID = 3904
|
|
Int4rangeArrayOID = 3905
|
|
NumrangeOID = 3906
|
|
NumrangeArrayOID = 3907
|
|
TsrangeOID = 3908
|
|
TsrangeArrayOID = 3909
|
|
TstzrangeOID = 3910
|
|
TstzrangeArrayOID = 3911
|
|
Int8rangeOID = 3926
|
|
Int8rangeArrayOID = 3927
|
|
Int4multirangeOID = 4451
|
|
NummultirangeOID = 4532
|
|
TsmultirangeOID = 4533
|
|
TstzmultirangeOID = 4534
|
|
DatemultirangeOID = 4535
|
|
Int8multirangeOID = 4536
|
|
Int4multirangeArrayOID = 6150
|
|
NummultirangeArrayOID = 6151
|
|
TsmultirangeArrayOID = 6152
|
|
TstzmultirangeArrayOID = 6153
|
|
DatemultirangeArrayOID = 6155
|
|
Int8multirangeArrayOID = 6157
|
|
)
|
|
|
|
type InfinityModifier int8
|
|
|
|
const (
|
|
Infinity InfinityModifier = 1
|
|
Finite InfinityModifier = 0
|
|
NegativeInfinity InfinityModifier = -Infinity
|
|
)
|
|
|
|
func (im InfinityModifier) String() string {
|
|
switch im {
|
|
case Finite:
|
|
return "finite"
|
|
case Infinity:
|
|
return "infinity"
|
|
case NegativeInfinity:
|
|
return "-infinity"
|
|
default:
|
|
return "invalid"
|
|
}
|
|
}
|
|
|
|
// PostgreSQL format codes
|
|
const (
|
|
TextFormatCode = 0
|
|
BinaryFormatCode = 1
|
|
)
|
|
|
|
// A Codec converts between Go and PostgreSQL values.
|
|
type Codec interface {
|
|
// FormatSupported returns true if the format is supported.
|
|
FormatSupported(int16) bool
|
|
|
|
// PreferredFormat returns the preferred format.
|
|
PreferredFormat() int16
|
|
|
|
// PlanEncode returns an EncodePlan for encoding value into PostgreSQL format for oid and format. If no plan can be
|
|
// found then nil is returned.
|
|
PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan
|
|
|
|
// PlanScan returns a ScanPlan for scanning a PostgreSQL value into a destination with the same type as target. If
|
|
// no plan can be found then nil is returned.
|
|
PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan
|
|
|
|
// DecodeDatabaseSQLValue returns src decoded into a value compatible with the sql.Scanner interface.
|
|
DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error)
|
|
|
|
// DecodeValue returns src decoded into its default format.
|
|
DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error)
|
|
}
|
|
|
|
type nullAssignmentError struct {
|
|
dst any
|
|
}
|
|
|
|
func (e *nullAssignmentError) Error() string {
|
|
return fmt.Sprintf("cannot assign NULL to %T", e.dst)
|
|
}
|
|
|
|
type Type struct {
|
|
Codec Codec
|
|
Name string
|
|
OID uint32
|
|
}
|
|
|
|
// Map is the mapping between PostgreSQL server types and Go type handling logic. It can encode values for
|
|
// transmission to a PostgreSQL server and scan received values.
|
|
type Map struct {
|
|
oidToType map[uint32]*Type
|
|
nameToType map[string]*Type
|
|
reflectTypeToName map[reflect.Type]string
|
|
oidToFormatCode map[uint32]int16
|
|
|
|
reflectTypeToType map[reflect.Type]*Type
|
|
|
|
memoizedScanPlans map[uint32]map[reflect.Type][2]ScanPlan
|
|
|
|
// TryWrapEncodePlanFuncs is a slice of functions that will wrap a value that cannot be encoded by the Codec. Every
|
|
// time a wrapper is found the PlanEncode method will be recursively called with the new value. This allows several layers of wrappers
|
|
// to be built up. There are default functions placed in this slice by NewMap(). In most cases these functions
|
|
// should run last. i.e. Additional functions should typically be prepended not appended.
|
|
TryWrapEncodePlanFuncs []TryWrapEncodePlanFunc
|
|
|
|
// TryWrapScanPlanFuncs is a slice of functions that will wrap a target that cannot be scanned into by the Codec. Every
|
|
// time a wrapper is found the PlanScan method will be recursively called with the new target. This allows several layers of wrappers
|
|
// to be built up. There are default functions placed in this slice by NewMap(). In most cases these functions
|
|
// should run last. i.e. Additional functions should typically be prepended not appended.
|
|
TryWrapScanPlanFuncs []TryWrapScanPlanFunc
|
|
}
|
|
|
|
func NewMap() *Map {
|
|
m := &Map{
|
|
oidToType: make(map[uint32]*Type),
|
|
nameToType: make(map[string]*Type),
|
|
reflectTypeToName: make(map[reflect.Type]string),
|
|
oidToFormatCode: make(map[uint32]int16),
|
|
|
|
memoizedScanPlans: make(map[uint32]map[reflect.Type][2]ScanPlan),
|
|
|
|
TryWrapEncodePlanFuncs: []TryWrapEncodePlanFunc{
|
|
TryWrapDerefPointerEncodePlan,
|
|
TryWrapBuiltinTypeEncodePlan,
|
|
TryWrapFindUnderlyingTypeEncodePlan,
|
|
TryWrapStructEncodePlan,
|
|
TryWrapSliceEncodePlan,
|
|
TryWrapMultiDimSliceEncodePlan,
|
|
TryWrapArrayEncodePlan,
|
|
},
|
|
|
|
TryWrapScanPlanFuncs: []TryWrapScanPlanFunc{
|
|
TryPointerPointerScanPlan,
|
|
TryWrapBuiltinTypeScanPlan,
|
|
TryFindUnderlyingTypeScanPlan,
|
|
TryWrapStructScanPlan,
|
|
TryWrapPtrSliceScanPlan,
|
|
TryWrapPtrMultiDimSliceScanPlan,
|
|
TryWrapPtrArrayScanPlan,
|
|
},
|
|
}
|
|
|
|
// Base types
|
|
m.RegisterType(&Type{Name: "aclitem", OID: ACLItemOID, Codec: &TextFormatOnlyCodec{TextCodec{}}})
|
|
m.RegisterType(&Type{Name: "bit", OID: BitOID, Codec: BitsCodec{}})
|
|
m.RegisterType(&Type{Name: "bool", OID: BoolOID, Codec: BoolCodec{}})
|
|
m.RegisterType(&Type{Name: "box", OID: BoxOID, Codec: BoxCodec{}})
|
|
m.RegisterType(&Type{Name: "bpchar", OID: BPCharOID, Codec: TextCodec{}})
|
|
m.RegisterType(&Type{Name: "bytea", OID: ByteaOID, Codec: ByteaCodec{}})
|
|
m.RegisterType(&Type{Name: "char", OID: QCharOID, Codec: QCharCodec{}})
|
|
m.RegisterType(&Type{Name: "cid", OID: CIDOID, Codec: Uint32Codec{}})
|
|
m.RegisterType(&Type{Name: "cidr", OID: CIDROID, Codec: InetCodec{}})
|
|
m.RegisterType(&Type{Name: "circle", OID: CircleOID, Codec: CircleCodec{}})
|
|
m.RegisterType(&Type{Name: "date", OID: DateOID, Codec: DateCodec{}})
|
|
m.RegisterType(&Type{Name: "float4", OID: Float4OID, Codec: Float4Codec{}})
|
|
m.RegisterType(&Type{Name: "float8", OID: Float8OID, Codec: Float8Codec{}})
|
|
m.RegisterType(&Type{Name: "inet", OID: InetOID, Codec: InetCodec{}})
|
|
m.RegisterType(&Type{Name: "int2", OID: Int2OID, Codec: Int2Codec{}})
|
|
m.RegisterType(&Type{Name: "int4", OID: Int4OID, Codec: Int4Codec{}})
|
|
m.RegisterType(&Type{Name: "int8", OID: Int8OID, Codec: Int8Codec{}})
|
|
m.RegisterType(&Type{Name: "interval", OID: IntervalOID, Codec: IntervalCodec{}})
|
|
m.RegisterType(&Type{Name: "json", OID: JSONOID, Codec: JSONCodec{}})
|
|
m.RegisterType(&Type{Name: "jsonb", OID: JSONBOID, Codec: JSONBCodec{}})
|
|
m.RegisterType(&Type{Name: "line", OID: LineOID, Codec: LineCodec{}})
|
|
m.RegisterType(&Type{Name: "lseg", OID: LsegOID, Codec: LsegCodec{}})
|
|
m.RegisterType(&Type{Name: "macaddr", OID: MacaddrOID, Codec: MacaddrCodec{}})
|
|
m.RegisterType(&Type{Name: "name", OID: NameOID, Codec: TextCodec{}})
|
|
m.RegisterType(&Type{Name: "numeric", OID: NumericOID, Codec: NumericCodec{}})
|
|
m.RegisterType(&Type{Name: "oid", OID: OIDOID, Codec: Uint32Codec{}})
|
|
m.RegisterType(&Type{Name: "path", OID: PathOID, Codec: PathCodec{}})
|
|
m.RegisterType(&Type{Name: "point", OID: PointOID, Codec: PointCodec{}})
|
|
m.RegisterType(&Type{Name: "polygon", OID: PolygonOID, Codec: PolygonCodec{}})
|
|
m.RegisterType(&Type{Name: "record", OID: RecordOID, Codec: RecordCodec{}})
|
|
m.RegisterType(&Type{Name: "text", OID: TextOID, Codec: TextCodec{}})
|
|
m.RegisterType(&Type{Name: "tid", OID: TIDOID, Codec: TIDCodec{}})
|
|
m.RegisterType(&Type{Name: "time", OID: TimeOID, Codec: TimeCodec{}})
|
|
m.RegisterType(&Type{Name: "timestamp", OID: TimestampOID, Codec: TimestampCodec{}})
|
|
m.RegisterType(&Type{Name: "timestamptz", OID: TimestamptzOID, Codec: TimestamptzCodec{}})
|
|
m.RegisterType(&Type{Name: "unknown", OID: UnknownOID, Codec: TextCodec{}})
|
|
m.RegisterType(&Type{Name: "uuid", OID: UUIDOID, Codec: UUIDCodec{}})
|
|
m.RegisterType(&Type{Name: "varbit", OID: VarbitOID, Codec: BitsCodec{}})
|
|
m.RegisterType(&Type{Name: "varchar", OID: VarcharOID, Codec: TextCodec{}})
|
|
m.RegisterType(&Type{Name: "xid", OID: XIDOID, Codec: Uint32Codec{}})
|
|
|
|
// Range types
|
|
m.RegisterType(&Type{Name: "daterange", OID: DaterangeOID, Codec: &RangeCodec{ElementType: m.oidToType[DateOID]}})
|
|
m.RegisterType(&Type{Name: "int4range", OID: Int4rangeOID, Codec: &RangeCodec{ElementType: m.oidToType[Int4OID]}})
|
|
m.RegisterType(&Type{Name: "int8range", OID: Int8rangeOID, Codec: &RangeCodec{ElementType: m.oidToType[Int8OID]}})
|
|
m.RegisterType(&Type{Name: "numrange", OID: NumrangeOID, Codec: &RangeCodec{ElementType: m.oidToType[NumericOID]}})
|
|
m.RegisterType(&Type{Name: "tsrange", OID: TsrangeOID, Codec: &RangeCodec{ElementType: m.oidToType[TimestampOID]}})
|
|
m.RegisterType(&Type{Name: "tstzrange", OID: TstzrangeOID, Codec: &RangeCodec{ElementType: m.oidToType[TimestamptzOID]}})
|
|
|
|
// Multirange types
|
|
m.RegisterType(&Type{Name: "datemultirange", OID: DatemultirangeOID, Codec: &MultirangeCodec{ElementType: m.oidToType[DaterangeOID]}})
|
|
m.RegisterType(&Type{Name: "int4multirange", OID: Int4multirangeOID, Codec: &MultirangeCodec{ElementType: m.oidToType[Int4rangeOID]}})
|
|
m.RegisterType(&Type{Name: "int8multirange", OID: Int8multirangeOID, Codec: &MultirangeCodec{ElementType: m.oidToType[Int8rangeOID]}})
|
|
m.RegisterType(&Type{Name: "nummultirange", OID: NummultirangeOID, Codec: &MultirangeCodec{ElementType: m.oidToType[NumrangeOID]}})
|
|
m.RegisterType(&Type{Name: "tsmultirange", OID: TsmultirangeOID, Codec: &MultirangeCodec{ElementType: m.oidToType[TsrangeOID]}})
|
|
m.RegisterType(&Type{Name: "tstzmultirange", OID: TstzmultirangeOID, Codec: &MultirangeCodec{ElementType: m.oidToType[TstzrangeOID]}})
|
|
|
|
// Array types
|
|
m.RegisterType(&Type{Name: "_aclitem", OID: ACLItemArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[ACLItemOID]}})
|
|
m.RegisterType(&Type{Name: "_bit", OID: BitArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[BitOID]}})
|
|
m.RegisterType(&Type{Name: "_bool", OID: BoolArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[BoolOID]}})
|
|
m.RegisterType(&Type{Name: "_box", OID: BoxArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[BoxOID]}})
|
|
m.RegisterType(&Type{Name: "_bpchar", OID: BPCharArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[BPCharOID]}})
|
|
m.RegisterType(&Type{Name: "_bytea", OID: ByteaArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[ByteaOID]}})
|
|
m.RegisterType(&Type{Name: "_char", OID: QCharArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[QCharOID]}})
|
|
m.RegisterType(&Type{Name: "_cid", OID: CIDArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[CIDOID]}})
|
|
m.RegisterType(&Type{Name: "_cidr", OID: CIDRArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[CIDROID]}})
|
|
m.RegisterType(&Type{Name: "_circle", OID: CircleArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[CircleOID]}})
|
|
m.RegisterType(&Type{Name: "_date", OID: DateArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[DateOID]}})
|
|
m.RegisterType(&Type{Name: "_daterange", OID: DaterangeArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[DaterangeOID]}})
|
|
m.RegisterType(&Type{Name: "_float4", OID: Float4ArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[Float4OID]}})
|
|
m.RegisterType(&Type{Name: "_float8", OID: Float8ArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[Float8OID]}})
|
|
m.RegisterType(&Type{Name: "_inet", OID: InetArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[InetOID]}})
|
|
m.RegisterType(&Type{Name: "_int2", OID: Int2ArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[Int2OID]}})
|
|
m.RegisterType(&Type{Name: "_int4", OID: Int4ArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[Int4OID]}})
|
|
m.RegisterType(&Type{Name: "_int4range", OID: Int4rangeArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[Int4rangeOID]}})
|
|
m.RegisterType(&Type{Name: "_int8", OID: Int8ArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[Int8OID]}})
|
|
m.RegisterType(&Type{Name: "_int8range", OID: Int8rangeArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[Int8rangeOID]}})
|
|
m.RegisterType(&Type{Name: "_interval", OID: IntervalArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[IntervalOID]}})
|
|
m.RegisterType(&Type{Name: "_json", OID: JSONArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[JSONOID]}})
|
|
m.RegisterType(&Type{Name: "_jsonb", OID: JSONBArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[JSONBOID]}})
|
|
m.RegisterType(&Type{Name: "_line", OID: LineArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[LineOID]}})
|
|
m.RegisterType(&Type{Name: "_lseg", OID: LsegArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[LsegOID]}})
|
|
m.RegisterType(&Type{Name: "_macaddr", OID: MacaddrArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[MacaddrOID]}})
|
|
m.RegisterType(&Type{Name: "_name", OID: NameArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[NameOID]}})
|
|
m.RegisterType(&Type{Name: "_numeric", OID: NumericArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[NumericOID]}})
|
|
m.RegisterType(&Type{Name: "_numrange", OID: NumrangeArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[NumrangeOID]}})
|
|
m.RegisterType(&Type{Name: "_oid", OID: OIDArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[OIDOID]}})
|
|
m.RegisterType(&Type{Name: "_path", OID: PathArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[PathOID]}})
|
|
m.RegisterType(&Type{Name: "_point", OID: PointArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[PointOID]}})
|
|
m.RegisterType(&Type{Name: "_polygon", OID: PolygonArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[PolygonOID]}})
|
|
m.RegisterType(&Type{Name: "_record", OID: RecordArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[RecordOID]}})
|
|
m.RegisterType(&Type{Name: "_text", OID: TextArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[TextOID]}})
|
|
m.RegisterType(&Type{Name: "_tid", OID: TIDArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[TIDOID]}})
|
|
m.RegisterType(&Type{Name: "_time", OID: TimeArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[TimeOID]}})
|
|
m.RegisterType(&Type{Name: "_timestamp", OID: TimestampArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[TimestampOID]}})
|
|
m.RegisterType(&Type{Name: "_timestamptz", OID: TimestamptzArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[TimestamptzOID]}})
|
|
m.RegisterType(&Type{Name: "_tsrange", OID: TsrangeArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[TsrangeOID]}})
|
|
m.RegisterType(&Type{Name: "_tstzrange", OID: TstzrangeArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[TstzrangeOID]}})
|
|
m.RegisterType(&Type{Name: "_uuid", OID: UUIDArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[UUIDOID]}})
|
|
m.RegisterType(&Type{Name: "_varbit", OID: VarbitArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[VarbitOID]}})
|
|
m.RegisterType(&Type{Name: "_varchar", OID: VarcharArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[VarcharOID]}})
|
|
m.RegisterType(&Type{Name: "_xid", OID: XIDArrayOID, Codec: &ArrayCodec{ElementType: m.oidToType[XIDOID]}})
|
|
|
|
// Integer types that directly map to a PostgreSQL type
|
|
registerDefaultPgTypeVariants[int16](m, "int2")
|
|
registerDefaultPgTypeVariants[int32](m, "int4")
|
|
registerDefaultPgTypeVariants[int64](m, "int8")
|
|
|
|
// Integer types that do not have a direct match to a PostgreSQL type
|
|
registerDefaultPgTypeVariants[int8](m, "int8")
|
|
registerDefaultPgTypeVariants[int](m, "int8")
|
|
registerDefaultPgTypeVariants[uint8](m, "int8")
|
|
registerDefaultPgTypeVariants[uint16](m, "int8")
|
|
registerDefaultPgTypeVariants[uint32](m, "int8")
|
|
registerDefaultPgTypeVariants[uint64](m, "numeric")
|
|
registerDefaultPgTypeVariants[uint](m, "numeric")
|
|
|
|
registerDefaultPgTypeVariants[float32](m, "float4")
|
|
registerDefaultPgTypeVariants[float64](m, "float8")
|
|
|
|
registerDefaultPgTypeVariants[bool](m, "bool")
|
|
registerDefaultPgTypeVariants[time.Time](m, "timestamptz")
|
|
registerDefaultPgTypeVariants[time.Duration](m, "interval")
|
|
registerDefaultPgTypeVariants[string](m, "text")
|
|
registerDefaultPgTypeVariants[[]byte](m, "bytea")
|
|
|
|
registerDefaultPgTypeVariants[net.IP](m, "inet")
|
|
registerDefaultPgTypeVariants[net.IPNet](m, "cidr")
|
|
registerDefaultPgTypeVariants[netip.Addr](m, "inet")
|
|
registerDefaultPgTypeVariants[netip.Prefix](m, "cidr")
|
|
|
|
// pgtype provided structs
|
|
registerDefaultPgTypeVariants[Bits](m, "varbit")
|
|
registerDefaultPgTypeVariants[Bool](m, "bool")
|
|
registerDefaultPgTypeVariants[Box](m, "box")
|
|
registerDefaultPgTypeVariants[Circle](m, "circle")
|
|
registerDefaultPgTypeVariants[Date](m, "date")
|
|
registerDefaultPgTypeVariants[Range[Date]](m, "daterange")
|
|
registerDefaultPgTypeVariants[Multirange[Range[Date]]](m, "datemultirange")
|
|
registerDefaultPgTypeVariants[Float4](m, "float4")
|
|
registerDefaultPgTypeVariants[Float8](m, "float8")
|
|
registerDefaultPgTypeVariants[Range[Float8]](m, "numrange") // There is no PostgreSQL builtin float8range so map it to numrange.
|
|
registerDefaultPgTypeVariants[Multirange[Range[Float8]]](m, "nummultirange") // There is no PostgreSQL builtin float8multirange so map it to nummultirange.
|
|
registerDefaultPgTypeVariants[Int2](m, "int2")
|
|
registerDefaultPgTypeVariants[Int4](m, "int4")
|
|
registerDefaultPgTypeVariants[Range[Int4]](m, "int4range")
|
|
registerDefaultPgTypeVariants[Multirange[Range[Int4]]](m, "int4multirange")
|
|
registerDefaultPgTypeVariants[Int8](m, "int8")
|
|
registerDefaultPgTypeVariants[Range[Int8]](m, "int8range")
|
|
registerDefaultPgTypeVariants[Multirange[Range[Int8]]](m, "int8multirange")
|
|
registerDefaultPgTypeVariants[Interval](m, "interval")
|
|
registerDefaultPgTypeVariants[Line](m, "line")
|
|
registerDefaultPgTypeVariants[Lseg](m, "lseg")
|
|
registerDefaultPgTypeVariants[Numeric](m, "numeric")
|
|
registerDefaultPgTypeVariants[Range[Numeric]](m, "numrange")
|
|
registerDefaultPgTypeVariants[Multirange[Range[Numeric]]](m, "nummultirange")
|
|
registerDefaultPgTypeVariants[Path](m, "path")
|
|
registerDefaultPgTypeVariants[Point](m, "point")
|
|
registerDefaultPgTypeVariants[Polygon](m, "polygon")
|
|
registerDefaultPgTypeVariants[TID](m, "tid")
|
|
registerDefaultPgTypeVariants[Text](m, "text")
|
|
registerDefaultPgTypeVariants[Time](m, "time")
|
|
registerDefaultPgTypeVariants[Timestamp](m, "timestamp")
|
|
registerDefaultPgTypeVariants[Timestamptz](m, "timestamptz")
|
|
registerDefaultPgTypeVariants[Range[Timestamp]](m, "tsrange")
|
|
registerDefaultPgTypeVariants[Multirange[Range[Timestamp]]](m, "tsmultirange")
|
|
registerDefaultPgTypeVariants[Range[Timestamptz]](m, "tstzrange")
|
|
registerDefaultPgTypeVariants[Multirange[Range[Timestamptz]]](m, "tstzmultirange")
|
|
registerDefaultPgTypeVariants[UUID](m, "uuid")
|
|
|
|
return m
|
|
}
|
|
|
|
func (m *Map) RegisterType(t *Type) {
|
|
m.oidToType[t.OID] = t
|
|
m.nameToType[t.Name] = t
|
|
m.oidToFormatCode[t.OID] = t.Codec.PreferredFormat()
|
|
|
|
// Invalidated by type registration
|
|
m.reflectTypeToType = nil
|
|
for k := range m.memoizedScanPlans {
|
|
delete(m.memoizedScanPlans, k)
|
|
}
|
|
}
|
|
|
|
// 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 TypeForValue to determine a suitable data type.
|
|
func (m *Map) RegisterDefaultPgType(value any, name string) {
|
|
m.reflectTypeToName[reflect.TypeOf(value)] = name
|
|
|
|
// Invalidated by type registration
|
|
m.reflectTypeToType = nil
|
|
for k := range m.memoizedScanPlans {
|
|
delete(m.memoizedScanPlans, k)
|
|
}
|
|
}
|
|
|
|
func (m *Map) TypeForOID(oid uint32) (*Type, bool) {
|
|
dt, ok := m.oidToType[oid]
|
|
return dt, ok
|
|
}
|
|
|
|
func (m *Map) TypeForName(name string) (*Type, bool) {
|
|
dt, ok := m.nameToType[name]
|
|
return dt, ok
|
|
}
|
|
|
|
func (m *Map) buildReflectTypeToType() {
|
|
m.reflectTypeToType = make(map[reflect.Type]*Type)
|
|
|
|
for reflectType, name := range m.reflectTypeToName {
|
|
if dt, ok := m.nameToType[name]; ok {
|
|
m.reflectTypeToType[reflectType] = dt
|
|
}
|
|
}
|
|
}
|
|
|
|
// TypeForValue finds a data type suitable for v. Use RegisterType to register types that can encode and decode
|
|
// themselves. Use RegisterDefaultPgType to register that can be handled by a registered data type.
|
|
func (m *Map) TypeForValue(v any) (*Type, bool) {
|
|
if m.reflectTypeToType == nil {
|
|
m.buildReflectTypeToType()
|
|
}
|
|
|
|
dt, ok := m.reflectTypeToType[reflect.TypeOf(v)]
|
|
return dt, ok
|
|
}
|
|
|
|
// FormatCodeForOID returns the preferred format code for type oid. If the type is not registered it returns the text
|
|
// format code.
|
|
func (m *Map) FormatCodeForOID(oid uint32) int16 {
|
|
fc, ok := m.oidToFormatCode[oid]
|
|
if ok {
|
|
return fc
|
|
}
|
|
return TextFormatCode
|
|
}
|
|
|
|
// EncodePlan is a precompiled plan to encode a particular type into a particular OID and format.
|
|
type EncodePlan interface {
|
|
// Encode appends the encoded bytes of value to buf. If value is the SQL value NULL then append nothing and return
|
|
// (nil, nil). The caller of Encode is responsible for writing the correct NULL value or the length of the data
|
|
// written.
|
|
Encode(value any, buf []byte) (newBuf []byte, err error)
|
|
}
|
|
|
|
// ScanPlan is a precompiled plan to scan into a type of destination.
|
|
type ScanPlan interface {
|
|
// Scan scans src into target. src is only valid during the call to Scan. The ScanPlan must not retain a reference to
|
|
// src.
|
|
Scan(src []byte, target any) error
|
|
}
|
|
|
|
type scanPlanCodecSQLScanner struct {
|
|
c Codec
|
|
m *Map
|
|
oid uint32
|
|
formatCode int16
|
|
}
|
|
|
|
func (plan *scanPlanCodecSQLScanner) Scan(src []byte, dst any) error {
|
|
value, err := plan.c.DecodeDatabaseSQLValue(plan.m, plan.oid, plan.formatCode, src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
scanner := dst.(sql.Scanner)
|
|
return scanner.Scan(value)
|
|
}
|
|
|
|
type scanPlanSQLScanner struct {
|
|
formatCode int16
|
|
}
|
|
|
|
func (plan *scanPlanSQLScanner) Scan(src []byte, dst any) 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 plan.formatCode == BinaryFormatCode {
|
|
return scanner.Scan(src)
|
|
} else {
|
|
return scanner.Scan(string(src))
|
|
}
|
|
}
|
|
|
|
type scanPlanString struct{}
|
|
|
|
func (scanPlanString) Scan(src []byte, dst any) error {
|
|
if src == nil {
|
|
return fmt.Errorf("cannot scan NULL into %T", dst)
|
|
}
|
|
|
|
p := (dst).(*string)
|
|
*p = string(src)
|
|
return nil
|
|
}
|
|
|
|
type scanPlanAnyTextToBytes struct{}
|
|
|
|
func (scanPlanAnyTextToBytes) Scan(src []byte, dst any) error {
|
|
dstBuf := dst.(*[]byte)
|
|
if src == nil {
|
|
*dstBuf = nil
|
|
return nil
|
|
}
|
|
|
|
*dstBuf = make([]byte, len(src))
|
|
copy(*dstBuf, src)
|
|
return nil
|
|
}
|
|
|
|
type scanPlanFail struct {
|
|
m *Map
|
|
oid uint32
|
|
formatCode int16
|
|
}
|
|
|
|
func (plan *scanPlanFail) Scan(src []byte, dst any) error {
|
|
// If src is NULL it might be possible to scan into dst even though it is the types are not compatible. While this
|
|
// may seem to be a contrived case it can occur when selecting NULL directly. PostgreSQL assigns it the type of text.
|
|
// It would be surprising to the caller to have to cast the NULL (e.g. `select null::int`). So try to figure out a
|
|
// compatible data type for dst and scan with that.
|
|
//
|
|
// See https://github.com/jackc/pgx/issues/1326
|
|
if src == nil {
|
|
// As a horrible hack try all types to find anything that can scan into dst.
|
|
for oid := range plan.m.oidToType {
|
|
// using planScan instead of Scan or PlanScan to avoid polluting the planned scan cache.
|
|
plan := plan.m.planScan(oid, plan.formatCode, dst)
|
|
if _, ok := plan.(*scanPlanFail); !ok {
|
|
return plan.Scan(src, dst)
|
|
}
|
|
}
|
|
}
|
|
|
|
var format string
|
|
switch plan.formatCode {
|
|
case TextFormatCode:
|
|
format = "text"
|
|
case BinaryFormatCode:
|
|
format = "binary"
|
|
default:
|
|
format = fmt.Sprintf("unknown %d", plan.formatCode)
|
|
}
|
|
|
|
var dataTypeName string
|
|
if t, ok := plan.m.oidToType[plan.oid]; ok {
|
|
dataTypeName = t.Name
|
|
} else {
|
|
dataTypeName = "unknown type"
|
|
}
|
|
|
|
return fmt.Errorf("cannot scan %s (OID %d) in %v format into %T", dataTypeName, plan.oid, format, dst)
|
|
}
|
|
|
|
// TryWrapScanPlanFunc is a function that tries to create a wrapper plan for target. If successful it returns a plan
|
|
// that will convert the target passed to Scan and then call the next plan. nextTarget is target as it will be converted
|
|
// by plan. It must be used to find another suitable ScanPlan. When it is found SetNext must be called on plan for it
|
|
// to be usabled. ok indicates if a suitable wrapper was found.
|
|
type TryWrapScanPlanFunc func(target any) (plan WrappedScanPlanNextSetter, nextTarget any, ok bool)
|
|
|
|
type pointerPointerScanPlan struct {
|
|
dstType reflect.Type
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *pointerPointerScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *pointerPointerScanPlan) Scan(src []byte, dst any) error {
|
|
el := reflect.ValueOf(dst).Elem()
|
|
if src == nil {
|
|
el.Set(reflect.Zero(el.Type()))
|
|
return nil
|
|
}
|
|
|
|
el.Set(reflect.New(el.Type().Elem()))
|
|
return plan.next.Scan(src, el.Interface())
|
|
}
|
|
|
|
// TryPointerPointerScanPlan handles a pointer to a pointer by setting the target to nil for SQL NULL and allocating and
|
|
// scanning for non-NULL.
|
|
func TryPointerPointerScanPlan(target any) (plan WrappedScanPlanNextSetter, nextTarget any, ok bool) {
|
|
if dstValue := reflect.ValueOf(target); dstValue.Kind() == reflect.Ptr {
|
|
elemValue := dstValue.Elem()
|
|
if elemValue.Kind() == reflect.Ptr {
|
|
plan = &pointerPointerScanPlan{dstType: dstValue.Type()}
|
|
return plan, reflect.Zero(elemValue.Type()).Interface(), true
|
|
}
|
|
}
|
|
|
|
return nil, nil, false
|
|
}
|
|
|
|
// SkipUnderlyingTypePlanner prevents PlanScan and PlanDecode from trying to use the underlying type.
|
|
type SkipUnderlyingTypePlanner interface {
|
|
SkipUnderlyingTypePlan()
|
|
}
|
|
|
|
var elemKindToPointerTypes map[reflect.Kind]reflect.Type = map[reflect.Kind]reflect.Type{
|
|
reflect.Int: reflect.TypeOf(new(int)),
|
|
reflect.Int8: reflect.TypeOf(new(int8)),
|
|
reflect.Int16: reflect.TypeOf(new(int16)),
|
|
reflect.Int32: reflect.TypeOf(new(int32)),
|
|
reflect.Int64: reflect.TypeOf(new(int64)),
|
|
reflect.Uint: reflect.TypeOf(new(uint)),
|
|
reflect.Uint8: reflect.TypeOf(new(uint8)),
|
|
reflect.Uint16: reflect.TypeOf(new(uint16)),
|
|
reflect.Uint32: reflect.TypeOf(new(uint32)),
|
|
reflect.Uint64: reflect.TypeOf(new(uint64)),
|
|
reflect.Float32: reflect.TypeOf(new(float32)),
|
|
reflect.Float64: reflect.TypeOf(new(float64)),
|
|
reflect.String: reflect.TypeOf(new(string)),
|
|
}
|
|
|
|
type underlyingTypeScanPlan struct {
|
|
dstType reflect.Type
|
|
nextDstType reflect.Type
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *underlyingTypeScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *underlyingTypeScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, reflect.ValueOf(dst).Convert(plan.nextDstType).Interface())
|
|
}
|
|
|
|
// TryFindUnderlyingTypeScanPlan tries to convert to a Go builtin type. e.g. If value was of type MyString and
|
|
// MyString was defined as a string then a wrapper plan would be returned that converts MyString to string.
|
|
func TryFindUnderlyingTypeScanPlan(dst any) (plan WrappedScanPlanNextSetter, nextDst any, ok bool) {
|
|
if _, ok := dst.(SkipUnderlyingTypePlanner); ok {
|
|
return nil, nil, false
|
|
}
|
|
|
|
dstValue := reflect.ValueOf(dst)
|
|
|
|
if dstValue.Kind() == reflect.Ptr {
|
|
var elemValue reflect.Value
|
|
if dstValue.IsNil() {
|
|
elemValue = reflect.New(dstValue.Type().Elem()).Elem()
|
|
} else {
|
|
elemValue = dstValue.Elem()
|
|
}
|
|
nextDstType := elemKindToPointerTypes[elemValue.Kind()]
|
|
if nextDstType == nil && elemValue.Kind() == reflect.Slice {
|
|
if elemValue.Type().Elem().Kind() == reflect.Uint8 {
|
|
var v *[]byte
|
|
nextDstType = reflect.TypeOf(v)
|
|
}
|
|
}
|
|
|
|
if nextDstType != nil && dstValue.Type() != nextDstType {
|
|
return &underlyingTypeScanPlan{dstType: dstValue.Type(), nextDstType: nextDstType}, dstValue.Convert(nextDstType).Interface(), true
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil, false
|
|
}
|
|
|
|
type WrappedScanPlanNextSetter interface {
|
|
SetNext(ScanPlan)
|
|
ScanPlan
|
|
}
|
|
|
|
// TryWrapBuiltinTypeScanPlan tries to wrap a builtin type with a wrapper that provides additional methods. e.g. If
|
|
// value was of type int32 then a wrapper plan would be returned that converts target to a value that implements
|
|
// Int64Scanner.
|
|
func TryWrapBuiltinTypeScanPlan(target any) (plan WrappedScanPlanNextSetter, nextDst any, ok bool) {
|
|
switch target := target.(type) {
|
|
case *int8:
|
|
return &wrapInt8ScanPlan{}, (*int8Wrapper)(target), true
|
|
case *int16:
|
|
return &wrapInt16ScanPlan{}, (*int16Wrapper)(target), true
|
|
case *int32:
|
|
return &wrapInt32ScanPlan{}, (*int32Wrapper)(target), true
|
|
case *int64:
|
|
return &wrapInt64ScanPlan{}, (*int64Wrapper)(target), true
|
|
case *int:
|
|
return &wrapIntScanPlan{}, (*intWrapper)(target), true
|
|
case *uint8:
|
|
return &wrapUint8ScanPlan{}, (*uint8Wrapper)(target), true
|
|
case *uint16:
|
|
return &wrapUint16ScanPlan{}, (*uint16Wrapper)(target), true
|
|
case *uint32:
|
|
return &wrapUint32ScanPlan{}, (*uint32Wrapper)(target), true
|
|
case *uint64:
|
|
return &wrapUint64ScanPlan{}, (*uint64Wrapper)(target), true
|
|
case *uint:
|
|
return &wrapUintScanPlan{}, (*uintWrapper)(target), true
|
|
case *float32:
|
|
return &wrapFloat32ScanPlan{}, (*float32Wrapper)(target), true
|
|
case *float64:
|
|
return &wrapFloat64ScanPlan{}, (*float64Wrapper)(target), true
|
|
case *string:
|
|
return &wrapStringScanPlan{}, (*stringWrapper)(target), true
|
|
case *time.Time:
|
|
return &wrapTimeScanPlan{}, (*timeWrapper)(target), true
|
|
case *time.Duration:
|
|
return &wrapDurationScanPlan{}, (*durationWrapper)(target), true
|
|
case *net.IPNet:
|
|
return &wrapNetIPNetScanPlan{}, (*netIPNetWrapper)(target), true
|
|
case *net.IP:
|
|
return &wrapNetIPScanPlan{}, (*netIPWrapper)(target), true
|
|
case *netip.Prefix:
|
|
return &wrapNetipPrefixScanPlan{}, (*netipPrefixWrapper)(target), true
|
|
case *netip.Addr:
|
|
return &wrapNetipAddrScanPlan{}, (*netipAddrWrapper)(target), true
|
|
case *map[string]*string:
|
|
return &wrapMapStringToPointerStringScanPlan{}, (*mapStringToPointerStringWrapper)(target), true
|
|
case *map[string]string:
|
|
return &wrapMapStringToStringScanPlan{}, (*mapStringToStringWrapper)(target), true
|
|
case *[16]byte:
|
|
return &wrapByte16ScanPlan{}, (*byte16Wrapper)(target), true
|
|
case *[]byte:
|
|
return &wrapByteSliceScanPlan{}, (*byteSliceWrapper)(target), true
|
|
}
|
|
|
|
return nil, nil, false
|
|
}
|
|
|
|
type wrapInt8ScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapInt8ScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapInt8ScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*int8Wrapper)(dst.(*int8)))
|
|
}
|
|
|
|
type wrapInt16ScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapInt16ScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapInt16ScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*int16Wrapper)(dst.(*int16)))
|
|
}
|
|
|
|
type wrapInt32ScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapInt32ScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapInt32ScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*int32Wrapper)(dst.(*int32)))
|
|
}
|
|
|
|
type wrapInt64ScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapInt64ScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapInt64ScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*int64Wrapper)(dst.(*int64)))
|
|
}
|
|
|
|
type wrapIntScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapIntScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapIntScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*intWrapper)(dst.(*int)))
|
|
}
|
|
|
|
type wrapUint8ScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapUint8ScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapUint8ScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*uint8Wrapper)(dst.(*uint8)))
|
|
}
|
|
|
|
type wrapUint16ScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapUint16ScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapUint16ScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*uint16Wrapper)(dst.(*uint16)))
|
|
}
|
|
|
|
type wrapUint32ScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapUint32ScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapUint32ScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*uint32Wrapper)(dst.(*uint32)))
|
|
}
|
|
|
|
type wrapUint64ScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapUint64ScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapUint64ScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*uint64Wrapper)(dst.(*uint64)))
|
|
}
|
|
|
|
type wrapUintScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapUintScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapUintScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*uintWrapper)(dst.(*uint)))
|
|
}
|
|
|
|
type wrapFloat32ScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapFloat32ScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapFloat32ScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*float32Wrapper)(dst.(*float32)))
|
|
}
|
|
|
|
type wrapFloat64ScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapFloat64ScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapFloat64ScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*float64Wrapper)(dst.(*float64)))
|
|
}
|
|
|
|
type wrapStringScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapStringScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapStringScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*stringWrapper)(dst.(*string)))
|
|
}
|
|
|
|
type wrapTimeScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapTimeScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapTimeScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*timeWrapper)(dst.(*time.Time)))
|
|
}
|
|
|
|
type wrapDurationScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapDurationScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapDurationScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*durationWrapper)(dst.(*time.Duration)))
|
|
}
|
|
|
|
type wrapNetIPNetScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapNetIPNetScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapNetIPNetScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*netIPNetWrapper)(dst.(*net.IPNet)))
|
|
}
|
|
|
|
type wrapNetIPScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapNetIPScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapNetIPScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*netIPWrapper)(dst.(*net.IP)))
|
|
}
|
|
|
|
type wrapNetipPrefixScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapNetipPrefixScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapNetipPrefixScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*netipPrefixWrapper)(dst.(*netip.Prefix)))
|
|
}
|
|
|
|
type wrapNetipAddrScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapNetipAddrScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapNetipAddrScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*netipAddrWrapper)(dst.(*netip.Addr)))
|
|
}
|
|
|
|
type wrapMapStringToPointerStringScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapMapStringToPointerStringScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapMapStringToPointerStringScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*mapStringToPointerStringWrapper)(dst.(*map[string]*string)))
|
|
}
|
|
|
|
type wrapMapStringToStringScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapMapStringToStringScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapMapStringToStringScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*mapStringToStringWrapper)(dst.(*map[string]string)))
|
|
}
|
|
|
|
type wrapByte16ScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapByte16ScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapByte16ScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*byte16Wrapper)(dst.(*[16]byte)))
|
|
}
|
|
|
|
type wrapByteSliceScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapByteSliceScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapByteSliceScanPlan) Scan(src []byte, dst any) error {
|
|
return plan.next.Scan(src, (*byteSliceWrapper)(dst.(*[]byte)))
|
|
}
|
|
|
|
type pointerEmptyInterfaceScanPlan struct {
|
|
codec Codec
|
|
m *Map
|
|
oid uint32
|
|
formatCode int16
|
|
}
|
|
|
|
func (plan *pointerEmptyInterfaceScanPlan) Scan(src []byte, dst any) error {
|
|
value, err := plan.codec.DecodeValue(plan.m, plan.oid, plan.formatCode, src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ptrAny := dst.(*any)
|
|
*ptrAny = value
|
|
|
|
return nil
|
|
}
|
|
|
|
// TryWrapStructPlan tries to wrap a struct with a wrapper that implements CompositeIndexGetter.
|
|
func TryWrapStructScanPlan(target any) (plan WrappedScanPlanNextSetter, nextValue any, ok bool) {
|
|
targetValue := reflect.ValueOf(target)
|
|
if targetValue.Kind() != reflect.Ptr {
|
|
return nil, nil, false
|
|
}
|
|
|
|
var targetElemValue reflect.Value
|
|
if targetValue.IsNil() {
|
|
targetElemValue = reflect.New(targetValue.Type().Elem())
|
|
} else {
|
|
targetElemValue = targetValue.Elem()
|
|
}
|
|
targetElemType := targetElemValue.Type()
|
|
|
|
if targetElemType.Kind() == reflect.Struct {
|
|
exportedFields := getExportedFieldValues(targetElemValue)
|
|
if len(exportedFields) == 0 {
|
|
return nil, nil, false
|
|
}
|
|
|
|
w := ptrStructWrapper{
|
|
s: target,
|
|
exportedFields: exportedFields,
|
|
}
|
|
return &wrapAnyPtrStructScanPlan{}, &w, true
|
|
}
|
|
|
|
return nil, nil, false
|
|
}
|
|
|
|
type wrapAnyPtrStructScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapAnyPtrStructScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapAnyPtrStructScanPlan) Scan(src []byte, target any) error {
|
|
w := ptrStructWrapper{
|
|
s: target,
|
|
exportedFields: getExportedFieldValues(reflect.ValueOf(target).Elem()),
|
|
}
|
|
|
|
return plan.next.Scan(src, &w)
|
|
}
|
|
|
|
// TryWrapPtrSliceScanPlan tries to wrap a pointer to a single dimension slice.
|
|
func TryWrapPtrSliceScanPlan(target any) (plan WrappedScanPlanNextSetter, nextValue any, ok bool) {
|
|
// Avoid using reflect path for common types.
|
|
switch target := target.(type) {
|
|
case *[]int16:
|
|
return &wrapPtrSliceScanPlan[int16]{}, (*FlatArray[int16])(target), true
|
|
case *[]int32:
|
|
return &wrapPtrSliceScanPlan[int32]{}, (*FlatArray[int32])(target), true
|
|
case *[]int64:
|
|
return &wrapPtrSliceScanPlan[int64]{}, (*FlatArray[int64])(target), true
|
|
case *[]float32:
|
|
return &wrapPtrSliceScanPlan[float32]{}, (*FlatArray[float32])(target), true
|
|
case *[]float64:
|
|
return &wrapPtrSliceScanPlan[float64]{}, (*FlatArray[float64])(target), true
|
|
case *[]string:
|
|
return &wrapPtrSliceScanPlan[string]{}, (*FlatArray[string])(target), true
|
|
case *[]time.Time:
|
|
return &wrapPtrSliceScanPlan[time.Time]{}, (*FlatArray[time.Time])(target), true
|
|
}
|
|
|
|
targetValue := reflect.ValueOf(target)
|
|
if targetValue.Kind() != reflect.Ptr {
|
|
return nil, nil, false
|
|
}
|
|
|
|
targetElemValue := targetValue.Elem()
|
|
|
|
if targetElemValue.Kind() == reflect.Slice {
|
|
return &wrapPtrSliceReflectScanPlan{}, &anySliceArrayReflect{slice: targetElemValue}, true
|
|
}
|
|
return nil, nil, false
|
|
}
|
|
|
|
type wrapPtrSliceScanPlan[T any] struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapPtrSliceScanPlan[T]) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapPtrSliceScanPlan[T]) Scan(src []byte, target any) error {
|
|
return plan.next.Scan(src, (*FlatArray[T])(target.(*[]T)))
|
|
}
|
|
|
|
type wrapPtrSliceReflectScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapPtrSliceReflectScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapPtrSliceReflectScanPlan) Scan(src []byte, target any) error {
|
|
return plan.next.Scan(src, &anySliceArrayReflect{slice: reflect.ValueOf(target).Elem()})
|
|
}
|
|
|
|
// TryWrapPtrMultiDimSliceScanPlan tries to wrap a pointer to a multi-dimension slice.
|
|
func TryWrapPtrMultiDimSliceScanPlan(target any) (plan WrappedScanPlanNextSetter, nextValue any, ok bool) {
|
|
targetValue := reflect.ValueOf(target)
|
|
if targetValue.Kind() != reflect.Ptr {
|
|
return nil, nil, false
|
|
}
|
|
|
|
targetElemValue := targetValue.Elem()
|
|
|
|
if targetElemValue.Kind() == reflect.Slice {
|
|
elemElemKind := targetElemValue.Type().Elem().Kind()
|
|
if elemElemKind == reflect.Slice {
|
|
if !isRagged(targetElemValue) {
|
|
return &wrapPtrMultiDimSliceScanPlan{}, &anyMultiDimSliceArray{slice: targetValue.Elem()}, true
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, nil, false
|
|
}
|
|
|
|
type wrapPtrMultiDimSliceScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapPtrMultiDimSliceScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapPtrMultiDimSliceScanPlan) Scan(src []byte, target any) error {
|
|
return plan.next.Scan(src, &anyMultiDimSliceArray{slice: reflect.ValueOf(target).Elem()})
|
|
}
|
|
|
|
// TryWrapPtrArrayScanPlan tries to wrap a pointer to a single dimension array.
|
|
func TryWrapPtrArrayScanPlan(target any) (plan WrappedScanPlanNextSetter, nextValue any, ok bool) {
|
|
targetValue := reflect.ValueOf(target)
|
|
if targetValue.Kind() != reflect.Ptr {
|
|
return nil, nil, false
|
|
}
|
|
|
|
targetElemValue := targetValue.Elem()
|
|
|
|
if targetElemValue.Kind() == reflect.Array {
|
|
return &wrapPtrArrayReflectScanPlan{}, &anyArrayArrayReflect{array: targetElemValue}, true
|
|
}
|
|
return nil, nil, false
|
|
}
|
|
|
|
type wrapPtrArrayReflectScanPlan struct {
|
|
next ScanPlan
|
|
}
|
|
|
|
func (plan *wrapPtrArrayReflectScanPlan) SetNext(next ScanPlan) { plan.next = next }
|
|
|
|
func (plan *wrapPtrArrayReflectScanPlan) Scan(src []byte, target any) error {
|
|
return plan.next.Scan(src, &anyArrayArrayReflect{array: reflect.ValueOf(target).Elem()})
|
|
}
|
|
|
|
// PlanScan prepares a plan to scan a value into target.
|
|
func (m *Map) PlanScan(oid uint32, formatCode int16, target any) ScanPlan {
|
|
oidMemo := m.memoizedScanPlans[oid]
|
|
if oidMemo == nil {
|
|
oidMemo = make(map[reflect.Type][2]ScanPlan)
|
|
m.memoizedScanPlans[oid] = oidMemo
|
|
}
|
|
targetReflectType := reflect.TypeOf(target)
|
|
typeMemo := oidMemo[targetReflectType]
|
|
plan := typeMemo[formatCode]
|
|
if plan == nil {
|
|
plan = m.planScan(oid, formatCode, target)
|
|
typeMemo[formatCode] = plan
|
|
oidMemo[targetReflectType] = typeMemo
|
|
}
|
|
|
|
return plan
|
|
}
|
|
|
|
func (m *Map) planScan(oid uint32, formatCode int16, target any) ScanPlan {
|
|
if _, ok := target.(*UndecodedBytes); ok {
|
|
return scanPlanAnyToUndecodedBytes{}
|
|
}
|
|
|
|
switch formatCode {
|
|
case BinaryFormatCode:
|
|
switch target.(type) {
|
|
case *string:
|
|
switch oid {
|
|
case TextOID, VarcharOID:
|
|
return scanPlanString{}
|
|
}
|
|
}
|
|
case TextFormatCode:
|
|
switch target.(type) {
|
|
case *string:
|
|
return scanPlanString{}
|
|
case *[]byte:
|
|
if oid != ByteaOID {
|
|
return scanPlanAnyTextToBytes{}
|
|
}
|
|
case TextScanner:
|
|
return scanPlanTextAnyToTextScanner{}
|
|
}
|
|
}
|
|
|
|
var dt *Type
|
|
|
|
if dataType, ok := m.TypeForOID(oid); ok {
|
|
dt = dataType
|
|
} else if dataType, ok := m.TypeForValue(target); ok {
|
|
dt = dataType
|
|
oid = dt.OID // Preserve assumed OID in case we are recursively called below.
|
|
}
|
|
|
|
if dt != nil {
|
|
if plan := dt.Codec.PlanScan(m, oid, formatCode, target); plan != nil {
|
|
return plan
|
|
}
|
|
}
|
|
|
|
for _, f := range m.TryWrapScanPlanFuncs {
|
|
if wrapperPlan, nextDst, ok := f(target); ok {
|
|
if nextPlan := m.planScan(oid, formatCode, nextDst); nextPlan != nil {
|
|
if _, failed := nextPlan.(*scanPlanFail); !failed {
|
|
wrapperPlan.SetNext(nextPlan)
|
|
return wrapperPlan
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if dt != nil {
|
|
if _, ok := target.(*any); ok {
|
|
return &pointerEmptyInterfaceScanPlan{codec: dt.Codec, m: m, oid: oid, formatCode: formatCode}
|
|
}
|
|
|
|
if _, ok := target.(sql.Scanner); ok {
|
|
return &scanPlanCodecSQLScanner{c: dt.Codec, m: m, oid: oid, formatCode: formatCode}
|
|
}
|
|
}
|
|
|
|
if _, ok := target.(sql.Scanner); ok {
|
|
return &scanPlanSQLScanner{formatCode: formatCode}
|
|
}
|
|
|
|
return &scanPlanFail{m: m, oid: oid, formatCode: formatCode}
|
|
}
|
|
|
|
func (m *Map) Scan(oid uint32, formatCode int16, src []byte, dst any) error {
|
|
if dst == nil {
|
|
return nil
|
|
}
|
|
|
|
plan := m.PlanScan(oid, formatCode, dst)
|
|
return plan.Scan(src, dst)
|
|
}
|
|
|
|
func scanUnknownType(oid uint32, formatCode int16, buf []byte, dest any) 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)
|
|
}
|
|
}
|
|
|
|
var ErrScanTargetTypeChanged = errors.New("scan target type changed")
|
|
|
|
func codecScan(codec Codec, m *Map, oid uint32, format int16, src []byte, dst any) error {
|
|
scanPlan := codec.PlanScan(m, oid, format, dst)
|
|
if scanPlan == nil {
|
|
return fmt.Errorf("PlanScan did not find a plan")
|
|
}
|
|
return scanPlan.Scan(src, dst)
|
|
}
|
|
|
|
func codecDecodeToTextFormat(codec Codec, m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
|
|
if src == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
if format == TextFormatCode {
|
|
return string(src), nil
|
|
} else {
|
|
value, err := codec.DecodeValue(m, oid, format, src)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buf, err := m.Encode(oid, TextFormatCode, value, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return string(buf), nil
|
|
}
|
|
}
|
|
|
|
// PlanEncode returns an Encode plan for encoding value into PostgreSQL format for oid and format. If no plan can be
|
|
// found then nil is returned.
|
|
func (m *Map) PlanEncode(oid uint32, format int16, value any) EncodePlan {
|
|
if format == TextFormatCode {
|
|
switch value.(type) {
|
|
case string:
|
|
return encodePlanStringToAnyTextFormat{}
|
|
case TextValuer:
|
|
return encodePlanTextValuerToAnyTextFormat{}
|
|
}
|
|
}
|
|
|
|
var dt *Type
|
|
if dataType, ok := m.TypeForOID(oid); ok {
|
|
dt = dataType
|
|
} else {
|
|
// If no type for the OID was found, then either it is unknowable (e.g. the simple protocol) or it is an
|
|
// unregistered type. In either case try to find the type and OID that matches the value (e.g. a []byte would be
|
|
// registered to PostgreSQL bytea).
|
|
if dataType, ok := m.TypeForValue(value); ok {
|
|
dt = dataType
|
|
oid = dt.OID // Preserve assumed OID in case we are recursively called below.
|
|
}
|
|
}
|
|
|
|
if dt != nil {
|
|
if plan := dt.Codec.PlanEncode(m, oid, format, value); plan != nil {
|
|
return plan
|
|
}
|
|
}
|
|
|
|
for _, f := range m.TryWrapEncodePlanFuncs {
|
|
if wrapperPlan, nextValue, ok := f(value); ok {
|
|
if nextPlan := m.PlanEncode(oid, format, nextValue); nextPlan != nil {
|
|
wrapperPlan.SetNext(nextPlan)
|
|
return wrapperPlan
|
|
}
|
|
}
|
|
}
|
|
|
|
if _, ok := value.(driver.Valuer); ok {
|
|
return &encodePlanDriverValuer{m: m, oid: oid, formatCode: format}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type encodePlanStringToAnyTextFormat struct{}
|
|
|
|
func (encodePlanStringToAnyTextFormat) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
s := value.(string)
|
|
return append(buf, s...), nil
|
|
}
|
|
|
|
type encodePlanTextValuerToAnyTextFormat struct{}
|
|
|
|
func (encodePlanTextValuerToAnyTextFormat) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
t, err := value.(TextValuer).TextValue()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !t.Valid {
|
|
return nil, nil
|
|
}
|
|
|
|
return append(buf, t.String...), nil
|
|
}
|
|
|
|
type encodePlanDriverValuer struct {
|
|
m *Map
|
|
oid uint32
|
|
formatCode int16
|
|
}
|
|
|
|
func (plan *encodePlanDriverValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
dv := value.(driver.Valuer)
|
|
if dv == nil {
|
|
return nil, nil
|
|
}
|
|
v, err := dv.Value()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if v == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
newBuf, err = plan.m.Encode(plan.oid, plan.formatCode, v, buf)
|
|
if err == nil {
|
|
return newBuf, nil
|
|
}
|
|
|
|
s, ok := v.(string)
|
|
if !ok {
|
|
return nil, err
|
|
}
|
|
|
|
var scannedValue any
|
|
scanErr := plan.m.Scan(plan.oid, TextFormatCode, []byte(s), &scannedValue)
|
|
if scanErr != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Prevent infinite loop. We can't encode this. See https://github.com/jackc/pgx/issues/1331.
|
|
if reflect.TypeOf(value) == reflect.TypeOf(scannedValue) {
|
|
return nil, fmt.Errorf("tried to encode %v via encoding to text and scanning but failed due to receiving same type back", value)
|
|
}
|
|
|
|
var err2 error
|
|
newBuf, err2 = plan.m.Encode(plan.oid, BinaryFormatCode, scannedValue, buf)
|
|
if err2 != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return newBuf, nil
|
|
}
|
|
|
|
// TryWrapEncodePlanFunc is a function that tries to create a wrapper plan for value. If successful it returns a plan
|
|
// that will convert the value passed to Encode and then call the next plan. nextValue is value as it will be converted
|
|
// by plan. It must be used to find another suitable EncodePlan. When it is found SetNext must be called on plan for it
|
|
// to be usabled. ok indicates if a suitable wrapper was found.
|
|
type TryWrapEncodePlanFunc func(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool)
|
|
|
|
type derefPointerEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *derefPointerEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *derefPointerEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
ptr := reflect.ValueOf(value)
|
|
|
|
if ptr.IsNil() {
|
|
return nil, nil
|
|
}
|
|
|
|
return plan.next.Encode(ptr.Elem().Interface(), buf)
|
|
}
|
|
|
|
// TryWrapDerefPointerEncodePlan tries to dereference a pointer. e.g. If value was of type *string then a wrapper plan
|
|
// would be returned that derefences the value.
|
|
func TryWrapDerefPointerEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {
|
|
if _, ok := value.(driver.Valuer); ok {
|
|
return nil, nil, false
|
|
}
|
|
|
|
if valueType := reflect.TypeOf(value); valueType != nil && valueType.Kind() == reflect.Ptr {
|
|
return &derefPointerEncodePlan{}, reflect.New(valueType.Elem()).Elem().Interface(), true
|
|
}
|
|
|
|
return nil, nil, false
|
|
}
|
|
|
|
var kindToTypes map[reflect.Kind]reflect.Type = map[reflect.Kind]reflect.Type{
|
|
reflect.Int: reflect.TypeOf(int(0)),
|
|
reflect.Int8: reflect.TypeOf(int8(0)),
|
|
reflect.Int16: reflect.TypeOf(int16(0)),
|
|
reflect.Int32: reflect.TypeOf(int32(0)),
|
|
reflect.Int64: reflect.TypeOf(int64(0)),
|
|
reflect.Uint: reflect.TypeOf(uint(0)),
|
|
reflect.Uint8: reflect.TypeOf(uint8(0)),
|
|
reflect.Uint16: reflect.TypeOf(uint16(0)),
|
|
reflect.Uint32: reflect.TypeOf(uint32(0)),
|
|
reflect.Uint64: reflect.TypeOf(uint64(0)),
|
|
reflect.Float32: reflect.TypeOf(float32(0)),
|
|
reflect.Float64: reflect.TypeOf(float64(0)),
|
|
reflect.String: reflect.TypeOf(""),
|
|
}
|
|
|
|
type underlyingTypeEncodePlan struct {
|
|
nextValueType reflect.Type
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *underlyingTypeEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *underlyingTypeEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(reflect.ValueOf(value).Convert(plan.nextValueType).Interface(), buf)
|
|
}
|
|
|
|
// TryWrapFindUnderlyingTypeEncodePlan tries to convert to a Go builtin type. e.g. If value was of type MyString and
|
|
// MyString was defined as a string then a wrapper plan would be returned that converts MyString to string.
|
|
func TryWrapFindUnderlyingTypeEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {
|
|
if _, ok := value.(driver.Valuer); ok {
|
|
return nil, nil, false
|
|
}
|
|
|
|
if _, ok := value.(SkipUnderlyingTypePlanner); ok {
|
|
return nil, nil, false
|
|
}
|
|
|
|
refValue := reflect.ValueOf(value)
|
|
|
|
nextValueType := kindToTypes[refValue.Kind()]
|
|
if nextValueType != nil && refValue.Type() != nextValueType {
|
|
return &underlyingTypeEncodePlan{nextValueType: nextValueType}, refValue.Convert(nextValueType).Interface(), true
|
|
}
|
|
|
|
return nil, nil, false
|
|
}
|
|
|
|
type WrappedEncodePlanNextSetter interface {
|
|
SetNext(EncodePlan)
|
|
EncodePlan
|
|
}
|
|
|
|
// TryWrapBuiltinTypeEncodePlan tries to wrap a builtin type with a wrapper that provides additional methods. e.g. If
|
|
// value was of type int32 then a wrapper plan would be returned that converts value to a type that implements
|
|
// Int64Valuer.
|
|
func TryWrapBuiltinTypeEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {
|
|
if _, ok := value.(driver.Valuer); ok {
|
|
return nil, nil, false
|
|
}
|
|
|
|
switch value := value.(type) {
|
|
case int8:
|
|
return &wrapInt8EncodePlan{}, int8Wrapper(value), true
|
|
case int16:
|
|
return &wrapInt16EncodePlan{}, int16Wrapper(value), true
|
|
case int32:
|
|
return &wrapInt32EncodePlan{}, int32Wrapper(value), true
|
|
case int64:
|
|
return &wrapInt64EncodePlan{}, int64Wrapper(value), true
|
|
case int:
|
|
return &wrapIntEncodePlan{}, intWrapper(value), true
|
|
case uint8:
|
|
return &wrapUint8EncodePlan{}, uint8Wrapper(value), true
|
|
case uint16:
|
|
return &wrapUint16EncodePlan{}, uint16Wrapper(value), true
|
|
case uint32:
|
|
return &wrapUint32EncodePlan{}, uint32Wrapper(value), true
|
|
case uint64:
|
|
return &wrapUint64EncodePlan{}, uint64Wrapper(value), true
|
|
case uint:
|
|
return &wrapUintEncodePlan{}, uintWrapper(value), true
|
|
case float32:
|
|
return &wrapFloat32EncodePlan{}, float32Wrapper(value), true
|
|
case float64:
|
|
return &wrapFloat64EncodePlan{}, float64Wrapper(value), true
|
|
case string:
|
|
return &wrapStringEncodePlan{}, stringWrapper(value), true
|
|
case time.Time:
|
|
return &wrapTimeEncodePlan{}, timeWrapper(value), true
|
|
case time.Duration:
|
|
return &wrapDurationEncodePlan{}, durationWrapper(value), true
|
|
case net.IPNet:
|
|
return &wrapNetIPNetEncodePlan{}, netIPNetWrapper(value), true
|
|
case net.IP:
|
|
return &wrapNetIPEncodePlan{}, netIPWrapper(value), true
|
|
case netip.Prefix:
|
|
return &wrapNetipPrefixEncodePlan{}, netipPrefixWrapper(value), true
|
|
case netip.Addr:
|
|
return &wrapNetipAddrEncodePlan{}, netipAddrWrapper(value), true
|
|
case map[string]*string:
|
|
return &wrapMapStringToPointerStringEncodePlan{}, mapStringToPointerStringWrapper(value), true
|
|
case map[string]string:
|
|
return &wrapMapStringToStringEncodePlan{}, mapStringToStringWrapper(value), true
|
|
case [16]byte:
|
|
return &wrapByte16EncodePlan{}, byte16Wrapper(value), true
|
|
case []byte:
|
|
return &wrapByteSliceEncodePlan{}, byteSliceWrapper(value), true
|
|
case fmt.Stringer:
|
|
return &wrapFmtStringerEncodePlan{}, fmtStringerWrapper{value}, true
|
|
}
|
|
|
|
return nil, nil, false
|
|
}
|
|
|
|
type wrapInt8EncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapInt8EncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapInt8EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(int8Wrapper(value.(int8)), buf)
|
|
}
|
|
|
|
type wrapInt16EncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapInt16EncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapInt16EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(int16Wrapper(value.(int16)), buf)
|
|
}
|
|
|
|
type wrapInt32EncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapInt32EncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapInt32EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(int32Wrapper(value.(int32)), buf)
|
|
}
|
|
|
|
type wrapInt64EncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapInt64EncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapInt64EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(int64Wrapper(value.(int64)), buf)
|
|
}
|
|
|
|
type wrapIntEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapIntEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapIntEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(intWrapper(value.(int)), buf)
|
|
}
|
|
|
|
type wrapUint8EncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapUint8EncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapUint8EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(uint8Wrapper(value.(uint8)), buf)
|
|
}
|
|
|
|
type wrapUint16EncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapUint16EncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapUint16EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(uint16Wrapper(value.(uint16)), buf)
|
|
}
|
|
|
|
type wrapUint32EncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapUint32EncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapUint32EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(uint32Wrapper(value.(uint32)), buf)
|
|
}
|
|
|
|
type wrapUint64EncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapUint64EncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapUint64EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(uint64Wrapper(value.(uint64)), buf)
|
|
}
|
|
|
|
type wrapUintEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapUintEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapUintEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(uintWrapper(value.(uint)), buf)
|
|
}
|
|
|
|
type wrapFloat32EncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapFloat32EncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapFloat32EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(float32Wrapper(value.(float32)), buf)
|
|
}
|
|
|
|
type wrapFloat64EncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapFloat64EncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapFloat64EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(float64Wrapper(value.(float64)), buf)
|
|
}
|
|
|
|
type wrapStringEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapStringEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapStringEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(stringWrapper(value.(string)), buf)
|
|
}
|
|
|
|
type wrapTimeEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapTimeEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapTimeEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(timeWrapper(value.(time.Time)), buf)
|
|
}
|
|
|
|
type wrapDurationEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapDurationEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapDurationEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(durationWrapper(value.(time.Duration)), buf)
|
|
}
|
|
|
|
type wrapNetIPNetEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapNetIPNetEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapNetIPNetEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(netIPNetWrapper(value.(net.IPNet)), buf)
|
|
}
|
|
|
|
type wrapNetIPEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapNetIPEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapNetIPEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(netIPWrapper(value.(net.IP)), buf)
|
|
}
|
|
|
|
type wrapNetipPrefixEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapNetipPrefixEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapNetipPrefixEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(netipPrefixWrapper(value.(netip.Prefix)), buf)
|
|
}
|
|
|
|
type wrapNetipAddrEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapNetipAddrEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapNetipAddrEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(netipAddrWrapper(value.(netip.Addr)), buf)
|
|
}
|
|
|
|
type wrapMapStringToPointerStringEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapMapStringToPointerStringEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapMapStringToPointerStringEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(mapStringToPointerStringWrapper(value.(map[string]*string)), buf)
|
|
}
|
|
|
|
type wrapMapStringToStringEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapMapStringToStringEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapMapStringToStringEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(mapStringToStringWrapper(value.(map[string]string)), buf)
|
|
}
|
|
|
|
type wrapByte16EncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapByte16EncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapByte16EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(byte16Wrapper(value.([16]byte)), buf)
|
|
}
|
|
|
|
type wrapByteSliceEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapByteSliceEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapByteSliceEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(byteSliceWrapper(value.([]byte)), buf)
|
|
}
|
|
|
|
type wrapFmtStringerEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapFmtStringerEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapFmtStringerEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
return plan.next.Encode(fmtStringerWrapper{value.(fmt.Stringer)}, buf)
|
|
}
|
|
|
|
// TryWrapStructPlan tries to wrap a struct with a wrapper that implements CompositeIndexGetter.
|
|
func TryWrapStructEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {
|
|
if _, ok := value.(driver.Valuer); ok {
|
|
return nil, nil, false
|
|
}
|
|
|
|
if valueType := reflect.TypeOf(value); valueType != nil && valueType.Kind() == reflect.Struct {
|
|
exportedFields := getExportedFieldValues(reflect.ValueOf(value))
|
|
if len(exportedFields) == 0 {
|
|
return nil, nil, false
|
|
}
|
|
|
|
w := structWrapper{
|
|
s: value,
|
|
exportedFields: exportedFields,
|
|
}
|
|
return &wrapAnyStructEncodePlan{}, w, true
|
|
}
|
|
|
|
return nil, nil, false
|
|
}
|
|
|
|
type wrapAnyStructEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapAnyStructEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapAnyStructEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
w := structWrapper{
|
|
s: value,
|
|
exportedFields: getExportedFieldValues(reflect.ValueOf(value)),
|
|
}
|
|
|
|
return plan.next.Encode(w, buf)
|
|
}
|
|
|
|
func getExportedFieldValues(structValue reflect.Value) []reflect.Value {
|
|
structType := structValue.Type()
|
|
exportedFields := make([]reflect.Value, 0, structValue.NumField())
|
|
for i := 0; i < structType.NumField(); i++ {
|
|
sf := structType.Field(i)
|
|
if sf.IsExported() {
|
|
exportedFields = append(exportedFields, structValue.Field(i))
|
|
}
|
|
}
|
|
|
|
return exportedFields
|
|
}
|
|
|
|
func TryWrapSliceEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {
|
|
if _, ok := value.(driver.Valuer); ok {
|
|
return nil, nil, false
|
|
}
|
|
|
|
// Avoid using reflect path for common types.
|
|
switch value := value.(type) {
|
|
case []int16:
|
|
return &wrapSliceEncodePlan[int16]{}, (FlatArray[int16])(value), true
|
|
case []int32:
|
|
return &wrapSliceEncodePlan[int32]{}, (FlatArray[int32])(value), true
|
|
case []int64:
|
|
return &wrapSliceEncodePlan[int64]{}, (FlatArray[int64])(value), true
|
|
case []float32:
|
|
return &wrapSliceEncodePlan[float32]{}, (FlatArray[float32])(value), true
|
|
case []float64:
|
|
return &wrapSliceEncodePlan[float64]{}, (FlatArray[float64])(value), true
|
|
case []string:
|
|
return &wrapSliceEncodePlan[string]{}, (FlatArray[string])(value), true
|
|
case []time.Time:
|
|
return &wrapSliceEncodePlan[time.Time]{}, (FlatArray[time.Time])(value), true
|
|
}
|
|
|
|
if valueType := reflect.TypeOf(value); valueType != nil && valueType.Kind() == reflect.Slice {
|
|
w := anySliceArrayReflect{
|
|
slice: reflect.ValueOf(value),
|
|
}
|
|
return &wrapSliceEncodeReflectPlan{}, w, true
|
|
}
|
|
|
|
return nil, nil, false
|
|
}
|
|
|
|
type wrapSliceEncodePlan[T any] struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapSliceEncodePlan[T]) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapSliceEncodePlan[T]) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
w := anySliceArrayReflect{
|
|
slice: reflect.ValueOf(value),
|
|
}
|
|
|
|
return plan.next.Encode(w, buf)
|
|
}
|
|
|
|
type wrapSliceEncodeReflectPlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapSliceEncodeReflectPlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapSliceEncodeReflectPlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
w := anySliceArrayReflect{
|
|
slice: reflect.ValueOf(value),
|
|
}
|
|
|
|
return plan.next.Encode(w, buf)
|
|
}
|
|
|
|
func TryWrapMultiDimSliceEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {
|
|
if _, ok := value.(driver.Valuer); ok {
|
|
return nil, nil, false
|
|
}
|
|
|
|
sliceValue := reflect.ValueOf(value)
|
|
if sliceValue.Kind() == reflect.Slice {
|
|
valueElemType := sliceValue.Type().Elem()
|
|
|
|
if valueElemType.Kind() == reflect.Slice {
|
|
if !isRagged(sliceValue) {
|
|
w := anyMultiDimSliceArray{
|
|
slice: reflect.ValueOf(value),
|
|
}
|
|
return &wrapMultiDimSliceEncodePlan{}, &w, true
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, nil, false
|
|
}
|
|
|
|
type wrapMultiDimSliceEncodePlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapMultiDimSliceEncodePlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapMultiDimSliceEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
w := anyMultiDimSliceArray{
|
|
slice: reflect.ValueOf(value),
|
|
}
|
|
|
|
return plan.next.Encode(&w, buf)
|
|
}
|
|
|
|
func TryWrapArrayEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {
|
|
if _, ok := value.(driver.Valuer); ok {
|
|
return nil, nil, false
|
|
}
|
|
|
|
if valueType := reflect.TypeOf(value); valueType != nil && valueType.Kind() == reflect.Array {
|
|
w := anyArrayArrayReflect{
|
|
array: reflect.ValueOf(value),
|
|
}
|
|
return &wrapArrayEncodeReflectPlan{}, w, true
|
|
}
|
|
|
|
return nil, nil, false
|
|
}
|
|
|
|
type wrapArrayEncodeReflectPlan struct {
|
|
next EncodePlan
|
|
}
|
|
|
|
func (plan *wrapArrayEncodeReflectPlan) SetNext(next EncodePlan) { plan.next = next }
|
|
|
|
func (plan *wrapArrayEncodeReflectPlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
w := anyArrayArrayReflect{
|
|
array: reflect.ValueOf(value),
|
|
}
|
|
|
|
return plan.next.Encode(w, buf)
|
|
}
|
|
|
|
func newEncodeError(value any, m *Map, oid uint32, formatCode int16, err error) error {
|
|
var format string
|
|
switch formatCode {
|
|
case TextFormatCode:
|
|
format = "text"
|
|
case BinaryFormatCode:
|
|
format = "binary"
|
|
default:
|
|
format = fmt.Sprintf("unknown (%d)", formatCode)
|
|
}
|
|
|
|
var dataTypeName string
|
|
if t, ok := m.oidToType[oid]; ok {
|
|
dataTypeName = t.Name
|
|
} else {
|
|
dataTypeName = "unknown type"
|
|
}
|
|
|
|
return fmt.Errorf("unable to encode %#v into %s format for %s (OID %d): %s", value, format, dataTypeName, oid, err)
|
|
}
|
|
|
|
// Encode appends the encoded bytes of value to buf. If value is the SQL value NULL then append nothing and return
|
|
// (nil, nil). The caller of Encode is responsible for writing the correct NULL value or the length of the data
|
|
// written.
|
|
func (m *Map) Encode(oid uint32, formatCode int16, value any, buf []byte) (newBuf []byte, err error) {
|
|
if value == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
plan := m.PlanEncode(oid, formatCode, value)
|
|
if plan == nil {
|
|
return nil, newEncodeError(value, m, oid, formatCode, errors.New("cannot find encode plan"))
|
|
}
|
|
|
|
newBuf, err = plan.Encode(value, buf)
|
|
if err != nil {
|
|
return nil, newEncodeError(value, m, oid, formatCode, err)
|
|
}
|
|
|
|
return newBuf, nil
|
|
}
|
|
|
|
// SQLScanner returns a database/sql.Scanner for v. This is necessary for types like Array[T] and Range[T] where the
|
|
// type needs assistance from Map to implement the sql.Scanner interface. It is not necessary for types like Box that
|
|
// implement sql.Scanner directly.
|
|
//
|
|
// This uses the type of v to look up the PostgreSQL OID that v presumably came from. This means v must be registered
|
|
// with m by calling RegisterDefaultPgType.
|
|
func (m *Map) SQLScanner(v any) sql.Scanner {
|
|
if s, ok := v.(sql.Scanner); ok {
|
|
return s
|
|
}
|
|
|
|
return &sqlScannerWrapper{m: m, v: v}
|
|
}
|
|
|
|
type sqlScannerWrapper struct {
|
|
m *Map
|
|
v any
|
|
}
|
|
|
|
func (w *sqlScannerWrapper) Scan(src any) error {
|
|
t, ok := w.m.TypeForValue(w.v)
|
|
if !ok {
|
|
return fmt.Errorf("cannot convert to sql.Scanner: cannot find registered type for %T", w.v)
|
|
}
|
|
|
|
var bufSrc []byte
|
|
if src != nil {
|
|
switch src := src.(type) {
|
|
case string:
|
|
bufSrc = []byte(src)
|
|
case []byte:
|
|
bufSrc = src
|
|
default:
|
|
bufSrc = []byte(fmt.Sprint(bufSrc))
|
|
}
|
|
}
|
|
|
|
return w.m.Scan(t.OID, TextFormatCode, bufSrc, w.v)
|
|
}
|