Repository navigation
Expand file tree
/
Copy pathmessage.go
More file actions
235 lines (218 loc) · 5.54 KB
/
Copy pathmessage.go
File metadata and controls
235 lines (218 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
package pgproto
import (
"fmt"
"io"
)
// Message is the main interface for all PostgreSQL messages
type Message interface {
Encode() []byte
AsMap() map[string]interface{}
String() string
}
// ClientMessage is an interface describing all client side PostgreSQL messages (messages sent to the server)
type ClientMessage interface {
Message
client()
}
// ServerMessage is an interface describing all server side PostgreSQL messages (messages sent to the client)
type ServerMessage interface {
Message
server()
}
// ParseClientMessage will read the next ClientMessage from the provided io.Reader
func ParseClientMessage(r io.Reader) (ClientMessage, error) {
// Create a buffer
buf := newReadBuffer(r)
// Look at the first byte to determine the type of message we have
start, err := buf.ReadByte()
if err != nil {
return nil, err
}
// Startup message:
// [int32 - length] [int32 - protocol] [[string]\0[string]\0] \0
// Regular message
// [char - tag] [int32 - length] [payload]
switch start {
// TODO: We need to handle this case better, it might not always start with \x00
// We could just make calling `ParseStartupMessage` explicit
case '\x00':
msgReader, err := readStartupMessage(start, buf)
if err != nil {
return nil, err
}
return ParseStartupMessage(msgReader)
default:
// Read the entire next message from the input reader
msgReader, err := readMessage(start, buf)
if err != nil {
return nil, err
}
switch start {
case 'p':
// Password message
return ParsePasswordMessage(msgReader)
case 'Q':
// Simple query
return ParseSimpleQuery(msgReader)
case 't':
// Parameter description
return ParseParameterDescription(msgReader)
case 'B':
// Binary parameters
return ParseBinaryParameters(msgReader)
case 'P':
// Parse
return ParseParse(msgReader)
case 'E':
// Execute
return ParseExecute(msgReader)
case 'H':
// Flush
return ParseFlush(msgReader)
case 'S':
// Sync
return ParseSync(msgReader)
case 'C':
// Close
return ParseClose(msgReader)
case 'D':
// Describe
return ParseDescribe(msgReader)
case 'X':
// Termination
return ParseTermination(msgReader)
default:
return nil, fmt.Errorf("unknown message tag '%c'", start)
}
}
}
// ParseServerMessage will read the next ServerMessage from the provided io.Reader
func ParseServerMessage(r io.Reader) (ServerMessage, error) {
// Create a buffer
buf := newReadBuffer(r)
// Look at the first byte to determine the type of message we have
start, err := buf.ReadByte()
if err != nil {
return nil, err
}
// Read the entire next message from the input reader
msgReader, err := readMessage(start, buf)
if err != nil {
return nil, err
}
// Message
// [char - tag] [int32 - length] [payload]
switch start {
case 'R':
// Authentication request
return ParseAuthenticationRequest(msgReader)
case 'S':
// Parameter status
return ParseParameterStatus(msgReader)
case 'K':
// Backend key data
return ParseBackendKeyData(msgReader)
case 'Z':
// Ready for query
return ParseReadyForQuery(msgReader)
case 'C':
// Command completion
return ParseCommandCompletion(msgReader)
case 'T':
// Row description
return ParseRowDescription(msgReader)
case 't':
// Parameter description
return nil, fmt.Errorf("unhandled message tag %#v", start)
case 'D':
// Data row
return ParseDataRow(msgReader)
case 'I':
// Empty query response
return ParseEmptyQueryResponse(msgReader)
case '1':
// Parse complete
return ParseParseComplete(msgReader)
case '2':
// Bind complete
return ParseBindComplete(msgReader)
case '3':
// Close complete
return ParseCloseComplete(msgReader)
case 'W':
// Copy both response
return ParseCopyBothResponse(msgReader)
case 'd':
// Copy data
return ParseCopyData(msgReader)
case 'G':
// Copy in response
return ParseCopyInResponse(msgReader)
case 'H':
// Copy out response
return ParseCopyOutResponse(msgReader)
case 'V':
// Function call response
return nil, fmt.Errorf("unhandled message tag %#v", start)
case 'n':
// No data
return ParseNoData(msgReader)
case 'N':
// Notice response
return ParseNoticeResponse(msgReader)
case 'A':
// Notification response
return ParseNotification(msgReader)
case 'E':
// Error message
return ParseError(msgReader)
default:
return nil, fmt.Errorf("unknown message tag '%c'", start)
}
}
func readStartupMessage(start byte, buf *readBuffer) (io.Reader, error) {
// [int32 - length] [payload]
// StartupMessage
// Read the next 3 bytes, prepend with the 1 we already read to parse the length from this message
s := [4]byte{
start,
}
_, err := buf.Read(s[1:])
if err != nil {
return nil, err
}
l := bytesToInt(s[:])
// Read the rest of the message into a []byte
// DEV: Subtract 4 to account for the length of the in32 we just read
b := make([]byte, l-4)
_, err = buf.Read(b)
if err != nil {
return nil, err
}
// Rebuild the message into a []byte
w := newWriteBuffer()
w.WriteInt(l)
w.WriteBytes(b)
return w.Reader(), nil
}
func readMessage(start byte, buf *readBuffer) (io.Reader, error) {
// [char tag] [int32 length] [payload]
// Parse length from the message
l, err := buf.ReadInt()
if err != nil {
return nil, err
}
// Read the rest of the message into a []byte
// DEV: Subtract 4 to account for the length of the int32 we just read
b := make([]byte, l-4)
_, err = buf.Read(b)
if err != nil {
return nil, err
}
// Rebuild the message into a []byte
w := newWriteBuffer()
w.WriteByte(start)
w.WriteInt(l)
w.WriteBytes(b)
return w.Reader(), nil
}