1package pgx
2
3import (
4	"database/sql/driver"
5	"math"
6	"reflect"
7	"time"
8
9	"github.com/jackc/pgx/pgio"
10	"github.com/jackc/pgx/pgtype"
11)
12
13const (
14	copyData      = 'd'
15	copyFail      = 'f'
16	copyDone      = 'c'
17	varHeaderSize = 4
18)
19
20type FieldDescription struct {
21	Name            string
22	Table           pgtype.OID
23	AttributeNumber uint16
24	DataType        pgtype.OID
25	DataTypeSize    int16
26	DataTypeName    string
27	Modifier        int32
28	FormatCode      int16
29}
30
31func (fd FieldDescription) Length() (int64, bool) {
32	switch fd.DataType {
33	case pgtype.TextOID, pgtype.ByteaOID:
34		return math.MaxInt64, true
35	case pgtype.VarcharOID, pgtype.BPCharArrayOID:
36		return int64(fd.Modifier - varHeaderSize), true
37	default:
38		return 0, false
39	}
40}
41
42func (fd FieldDescription) PrecisionScale() (precision, scale int64, ok bool) {
43	switch fd.DataType {
44	case pgtype.NumericOID:
45		mod := fd.Modifier - varHeaderSize
46		precision = int64((mod >> 16) & 0xffff)
47		scale = int64(mod & 0xffff)
48		return precision, scale, true
49	default:
50		return 0, 0, false
51	}
52}
53
54func (fd FieldDescription) Type() reflect.Type {
55	switch fd.DataType {
56	case pgtype.Float8OID:
57		return reflect.TypeOf(float64(0))
58	case pgtype.Float4OID:
59		return reflect.TypeOf(float32(0))
60	case pgtype.Int8OID:
61		return reflect.TypeOf(int64(0))
62	case pgtype.Int4OID:
63		return reflect.TypeOf(int32(0))
64	case pgtype.Int2OID:
65		return reflect.TypeOf(int16(0))
66	case pgtype.VarcharOID, pgtype.BPCharArrayOID, pgtype.TextOID:
67		return reflect.TypeOf("")
68	case pgtype.BoolOID:
69		return reflect.TypeOf(false)
70	case pgtype.NumericOID:
71		return reflect.TypeOf(float64(0))
72	case pgtype.DateOID, pgtype.TimestampOID, pgtype.TimestamptzOID:
73		return reflect.TypeOf(time.Time{})
74	case pgtype.ByteaOID:
75		return reflect.TypeOf([]byte(nil))
76	default:
77		return reflect.TypeOf(new(interface{})).Elem()
78	}
79}
80
81// PgError represents an error reported by the PostgreSQL server. See
82// http://www.postgresql.org/docs/9.3/static/protocol-error-fields.html for
83// detailed field description.
84type PgError struct {
85	Severity         string
86	Code             string
87	Message          string
88	Detail           string
89	Hint             string
90	Position         int32
91	InternalPosition int32
92	InternalQuery    string
93	Where            string
94	SchemaName       string
95	TableName        string
96	ColumnName       string
97	DataTypeName     string
98	ConstraintName   string
99	File             string
100	Line             int32
101	Routine          string
102}
103
104func (pe PgError) Error() string {
105	return pe.Severity + ": " + pe.Message + " (SQLSTATE " + pe.Code + ")"
106}
107
108// Notice represents a notice response message reported by the PostgreSQL
109// server. Be aware that this is distinct from LISTEN/NOTIFY notification.
110type Notice PgError
111
112// appendParse appends a PostgreSQL wire protocol parse message to buf and returns it.
113func appendParse(buf []byte, name string, query string, parameterOIDs []pgtype.OID) []byte {
114	buf = append(buf, 'P')
115	sp := len(buf)
116	buf = pgio.AppendInt32(buf, -1)
117	buf = append(buf, name...)
118	buf = append(buf, 0)
119	buf = append(buf, query...)
120	buf = append(buf, 0)
121
122	buf = pgio.AppendInt16(buf, int16(len(parameterOIDs)))
123	for _, oid := range parameterOIDs {
124		buf = pgio.AppendUint32(buf, uint32(oid))
125	}
126	pgio.SetInt32(buf[sp:], int32(len(buf[sp:])))
127
128	return buf
129}
130
131// appendDescribe appends a PostgreSQL wire protocol describe message to buf and returns it.
132func appendDescribe(buf []byte, objectType byte, name string) []byte {
133	buf = append(buf, 'D')
134	sp := len(buf)
135	buf = pgio.AppendInt32(buf, -1)
136	buf = append(buf, objectType)
137	buf = append(buf, name...)
138	buf = append(buf, 0)
139	pgio.SetInt32(buf[sp:], int32(len(buf[sp:])))
140
141	return buf
142}
143
144// appendSync appends a PostgreSQL wire protocol sync message to buf and returns it.
145func appendSync(buf []byte) []byte {
146	buf = append(buf, 'S')
147	buf = pgio.AppendInt32(buf, 4)
148
149	return buf
150}
151
152// appendBind appends a PostgreSQL wire protocol bind message to buf and returns it.
153func appendBind(
154	buf []byte,
155	destinationPortal,
156	preparedStatement string,
157	connInfo *pgtype.ConnInfo,
158	parameterOIDs []pgtype.OID,
159	arguments []interface{},
160	resultFormatCodes []int16,
161) ([]byte, error) {
162	buf = append(buf, 'B')
163	sp := len(buf)
164	buf = pgio.AppendInt32(buf, -1)
165	buf = append(buf, destinationPortal...)
166	buf = append(buf, 0)
167	buf = append(buf, preparedStatement...)
168	buf = append(buf, 0)
169
170	var err error
171	arguments, err = convertDriverValuers(arguments)
172	if err != nil {
173		return nil, err
174	}
175
176	buf = pgio.AppendInt16(buf, int16(len(parameterOIDs)))
177	for i, oid := range parameterOIDs {
178		buf = pgio.AppendInt16(buf, chooseParameterFormatCode(connInfo, oid, arguments[i]))
179	}
180
181	buf = pgio.AppendInt16(buf, int16(len(arguments)))
182	for i, oid := range parameterOIDs {
183		var err error
184		buf, err = encodePreparedStatementArgument(connInfo, buf, oid, arguments[i])
185		if err != nil {
186			return nil, err
187		}
188	}
189
190	buf = pgio.AppendInt16(buf, int16(len(resultFormatCodes)))
191	for _, fc := range resultFormatCodes {
192		buf = pgio.AppendInt16(buf, fc)
193	}
194	pgio.SetInt32(buf[sp:], int32(len(buf[sp:])))
195
196	return buf, nil
197}
198
199func convertDriverValuers(args []interface{}) ([]interface{}, error) {
200	for i, arg := range args {
201		switch arg := arg.(type) {
202		case pgtype.BinaryEncoder:
203		case pgtype.TextEncoder:
204		case driver.Valuer:
205			v, err := callValuerValue(arg)
206			if err != nil {
207				return nil, err
208			}
209			args[i] = v
210		}
211	}
212	return args, nil
213}
214
215// appendExecute appends a PostgreSQL wire protocol execute message to buf and returns it.
216func appendExecute(buf []byte, portal string, maxRows uint32) []byte {
217	buf = append(buf, 'E')
218	sp := len(buf)
219	buf = pgio.AppendInt32(buf, -1)
220
221	buf = append(buf, portal...)
222	buf = append(buf, 0)
223	buf = pgio.AppendUint32(buf, maxRows)
224
225	pgio.SetInt32(buf[sp:], int32(len(buf[sp:])))
226
227	return buf
228}
229
230// appendQuery appends a PostgreSQL wire protocol query message to buf and returns it.
231func appendQuery(buf []byte, query string) []byte {
232	buf = append(buf, 'Q')
233	sp := len(buf)
234	buf = pgio.AppendInt32(buf, -1)
235	buf = append(buf, query...)
236	buf = append(buf, 0)
237	pgio.SetInt32(buf[sp:], int32(len(buf[sp:])))
238
239	return buf
240}
241