1package pgtype
2
3import (
4	"reflect"
5
6	"github.com/pkg/errors"
7)
8
9// PostgreSQL oids for common types
10const (
11	BoolOID             = 16
12	ByteaOID            = 17
13	CharOID             = 18
14	NameOID             = 19
15	Int8OID             = 20
16	Int2OID             = 21
17	Int4OID             = 23
18	TextOID             = 25
19	OIDOID              = 26
20	TIDOID              = 27
21	XIDOID              = 28
22	CIDOID              = 29
23	JSONOID             = 114
24	CIDROID             = 650
25	CIDRArrayOID        = 651
26	Float4OID           = 700
27	Float8OID           = 701
28	UnknownOID          = 705
29	InetOID             = 869
30	BoolArrayOID        = 1000
31	Int2ArrayOID        = 1005
32	Int4ArrayOID        = 1007
33	TextArrayOID        = 1009
34	ByteaArrayOID       = 1001
35	BPCharArrayOID      = 1014
36	VarcharArrayOID     = 1015
37	Int8ArrayOID        = 1016
38	Float4ArrayOID      = 1021
39	Float8ArrayOID      = 1022
40	ACLItemOID          = 1033
41	ACLItemArrayOID     = 1034
42	InetArrayOID        = 1041
43	BPCharOID           = 1042
44	VarcharOID          = 1043
45	DateOID             = 1082
46	TimestampOID        = 1114
47	TimestampArrayOID   = 1115
48	DateArrayOID        = 1182
49	TimestamptzOID      = 1184
50	TimestamptzArrayOID = 1185
51	NumericOID          = 1700
52	RecordOID           = 2249
53	UUIDOID             = 2950
54	UUIDArrayOID        = 2951
55	JSONBOID            = 3802
56)
57
58type Status byte
59
60const (
61	Undefined Status = iota
62	Null
63	Present
64)
65
66type InfinityModifier int8
67
68const (
69	Infinity         InfinityModifier = 1
70	None             InfinityModifier = 0
71	NegativeInfinity InfinityModifier = -Infinity
72)
73
74func (im InfinityModifier) String() string {
75	switch im {
76	case None:
77		return "none"
78	case Infinity:
79		return "infinity"
80	case NegativeInfinity:
81		return "-infinity"
82	default:
83		return "invalid"
84	}
85}
86
87type Value interface {
88	// Set converts and assigns src to itself.
89	Set(src interface{}) error
90
91	// Get returns the simplest representation of Value. If the Value is Null or
92	// Undefined that is the return value. If no simpler representation is
93	// possible, then Get() returns Value.
94	Get() interface{}
95
96	// AssignTo converts and assigns the Value to dst. It MUST make a deep copy of
97	// any reference types.
98	AssignTo(dst interface{}) error
99}
100
101type BinaryDecoder interface {
102	// DecodeBinary decodes src into BinaryDecoder. If src is nil then the
103	// original SQL value is NULL. BinaryDecoder takes ownership of src. The
104	// caller MUST not use it again.
105	DecodeBinary(ci *ConnInfo, src []byte) error
106}
107
108type TextDecoder interface {
109	// DecodeText decodes src into TextDecoder. If src is nil then the original
110	// SQL value is NULL. TextDecoder takes ownership of src. The caller MUST not
111	// use it again.
112	DecodeText(ci *ConnInfo, src []byte) error
113}
114
115// BinaryEncoder is implemented by types that can encode themselves into the
116// PostgreSQL binary wire format.
117type BinaryEncoder interface {
118	// EncodeBinary should append the binary format of self to buf. If self is the
119	// SQL value NULL then append nothing and return (nil, nil). The caller of
120	// EncodeBinary is responsible for writing the correct NULL value or the
121	// length of the data written.
122	EncodeBinary(ci *ConnInfo, buf []byte) (newBuf []byte, err error)
123}
124
125// TextEncoder is implemented by types that can encode themselves into the
126// PostgreSQL text wire format.
127type TextEncoder interface {
128	// EncodeText should append the text format of self to buf. If self is the
129	// SQL value NULL then append nothing and return (nil, nil). The caller of
130	// EncodeText is responsible for writing the correct NULL value or the
131	// length of the data written.
132	EncodeText(ci *ConnInfo, buf []byte) (newBuf []byte, err error)
133}
134
135var errUndefined = errors.New("cannot encode status undefined")
136var errBadStatus = errors.New("invalid status")
137
138type DataType struct {
139	Value Value
140	Name  string
141	OID   OID
142}
143
144type ConnInfo struct {
145	oidToDataType         map[OID]*DataType
146	nameToDataType        map[string]*DataType
147	reflectTypeToDataType map[reflect.Type]*DataType
148}
149
150func NewConnInfo() *ConnInfo {
151	return &ConnInfo{
152		oidToDataType:         make(map[OID]*DataType, 256),
153		nameToDataType:        make(map[string]*DataType, 256),
154		reflectTypeToDataType: make(map[reflect.Type]*DataType, 256),
155	}
156}
157
158func (ci *ConnInfo) InitializeDataTypes(nameOIDs map[string]OID) {
159	for name, oid := range nameOIDs {
160		var value Value
161		if t, ok := nameValues[name]; ok {
162			value = reflect.New(reflect.ValueOf(t).Elem().Type()).Interface().(Value)
163		} else {
164			value = &GenericText{}
165		}
166		ci.RegisterDataType(DataType{Value: value, Name: name, OID: oid})
167	}
168}
169
170func (ci *ConnInfo) RegisterDataType(t DataType) {
171	ci.oidToDataType[t.OID] = &t
172	ci.nameToDataType[t.Name] = &t
173	ci.reflectTypeToDataType[reflect.ValueOf(t.Value).Type()] = &t
174}
175
176func (ci *ConnInfo) DataTypeForOID(oid OID) (*DataType, bool) {
177	dt, ok := ci.oidToDataType[oid]
178	return dt, ok
179}
180
181func (ci *ConnInfo) DataTypeForName(name string) (*DataType, bool) {
182	dt, ok := ci.nameToDataType[name]
183	return dt, ok
184}
185
186func (ci *ConnInfo) DataTypeForValue(v Value) (*DataType, bool) {
187	dt, ok := ci.reflectTypeToDataType[reflect.ValueOf(v).Type()]
188	return dt, ok
189}
190
191// DeepCopy makes a deep copy of the ConnInfo.
192func (ci *ConnInfo) DeepCopy() *ConnInfo {
193	ci2 := &ConnInfo{
194		oidToDataType:         make(map[OID]*DataType, len(ci.oidToDataType)),
195		nameToDataType:        make(map[string]*DataType, len(ci.nameToDataType)),
196		reflectTypeToDataType: make(map[reflect.Type]*DataType, len(ci.reflectTypeToDataType)),
197	}
198
199	for _, dt := range ci.oidToDataType {
200		ci2.RegisterDataType(DataType{
201			Value: reflect.New(reflect.ValueOf(dt.Value).Elem().Type()).Interface().(Value),
202			Name:  dt.Name,
203			OID:   dt.OID,
204		})
205	}
206
207	return ci2
208}
209
210var nameValues map[string]Value
211
212func init() {
213	nameValues = map[string]Value{
214		"_aclitem":     &ACLItemArray{},
215		"_bool":        &BoolArray{},
216		"_bpchar":      &BPCharArray{},
217		"_bytea":       &ByteaArray{},
218		"_cidr":        &CIDRArray{},
219		"_date":        &DateArray{},
220		"_float4":      &Float4Array{},
221		"_float8":      &Float8Array{},
222		"_inet":        &InetArray{},
223		"_int2":        &Int2Array{},
224		"_int4":        &Int4Array{},
225		"_int8":        &Int8Array{},
226		"_numeric":     &NumericArray{},
227		"_text":        &TextArray{},
228		"_timestamp":   &TimestampArray{},
229		"_timestamptz": &TimestamptzArray{},
230		"_uuid":        &UUIDArray{},
231		"_varchar":     &VarcharArray{},
232		"aclitem":      &ACLItem{},
233		"bit":          &Bit{},
234		"bool":         &Bool{},
235		"box":          &Box{},
236		"bpchar":       &BPChar{},
237		"bytea":        &Bytea{},
238		"char":         &QChar{},
239		"cid":          &CID{},
240		"cidr":         &CIDR{},
241		"circle":       &Circle{},
242		"date":         &Date{},
243		"daterange":    &Daterange{},
244		"decimal":      &Decimal{},
245		"float4":       &Float4{},
246		"float8":       &Float8{},
247		"hstore":       &Hstore{},
248		"inet":         &Inet{},
249		"int2":         &Int2{},
250		"int4":         &Int4{},
251		"int4range":    &Int4range{},
252		"int8":         &Int8{},
253		"int8range":    &Int8range{},
254		"interval":     &Interval{},
255		"json":         &JSON{},
256		"jsonb":        &JSONB{},
257		"line":         &Line{},
258		"lseg":         &Lseg{},
259		"macaddr":      &Macaddr{},
260		"name":         &Name{},
261		"numeric":      &Numeric{},
262		"numrange":     &Numrange{},
263		"oid":          &OIDValue{},
264		"path":         &Path{},
265		"point":        &Point{},
266		"polygon":      &Polygon{},
267		"record":       &Record{},
268		"text":         &Text{},
269		"tid":          &TID{},
270		"timestamp":    &Timestamp{},
271		"timestamptz":  &Timestamptz{},
272		"tsrange":      &Tsrange{},
273		"tstzrange":    &Tstzrange{},
274		"unknown":      &Unknown{},
275		"uuid":         &UUID{},
276		"varbit":       &Varbit{},
277		"varchar":      &Varchar{},
278		"xid":          &XID{},
279	}
280}
281