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