forked from vapor/postgres-nio
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPostgresBackendMessage.swift
294 lines (264 loc) · 10.2 KB
/
PostgresBackendMessage.swift
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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
import NIOCore
//import struct Foundation.Data
/// A protocol to implement for all associated value in the `PostgresBackendMessage` enum
protocol PSQLMessagePayloadDecodable {
/// Decodes the associated value for a `PostgresBackendMessage` from the given `ByteBuffer`.
///
/// When the decoding is done all bytes in the given `ByteBuffer` must be consumed.
/// `buffer.readableBytes` must be `0`. In case of an error a `PartialDecodingError`
/// must be thrown.
///
/// - Parameter buffer: The `ByteBuffer` to read the message from. When done the `ByteBuffer`
/// must be fully consumed.
static func decode(from buffer: inout ByteBuffer) throws -> Self
}
/// A wire message that is created by a Postgres server to be consumed by Postgres client.
///
/// All messages are defined in the official Postgres Documentation in the section
/// [Frontend/Backend Protocol – Message Formats](https://www.postgresql.org/docs/13/protocol-message-formats.html)
enum PostgresBackendMessage {
typealias PayloadDecodable = PSQLMessagePayloadDecodable
case authentication(Authentication)
case backendKeyData(BackendKeyData)
case bindComplete
case closeComplete
case commandComplete(String)
case dataRow(DataRow)
case emptyQueryResponse
case error(ErrorResponse)
case noData
case notice(NoticeResponse)
case notification(NotificationResponse)
case parameterDescription(ParameterDescription)
case parameterStatus(ParameterStatus)
case parseComplete
case portalSuspended
case readyForQuery(TransactionState)
case rowDescription(RowDescription)
case sslSupported
case sslUnsupported
}
extension PostgresBackendMessage {
enum ID: RawRepresentable, Equatable {
typealias RawValue = UInt8
case authentication
case backendKeyData
case bindComplete
case closeComplete
case commandComplete
case copyData
case copyDone
case copyInResponse
case copyOutResponse
case copyBothResponse
case dataRow
case emptyQueryResponse
case error
case functionCallResponse
case negotiateProtocolVersion
case noData
case noticeResponse
case notificationResponse
case parameterDescription
case parameterStatus
case parseComplete
case portalSuspended
case readyForQuery
case rowDescription
init?(rawValue: UInt8) {
switch rawValue {
case UInt8(ascii: "R"):
self = .authentication
case UInt8(ascii: "K"):
self = .backendKeyData
case UInt8(ascii: "2"):
self = .bindComplete
case UInt8(ascii: "3"):
self = .closeComplete
case UInt8(ascii: "C"):
self = .commandComplete
case UInt8(ascii: "d"):
self = .copyData
case UInt8(ascii: "c"):
self = .copyDone
case UInt8(ascii: "G"):
self = .copyInResponse
case UInt8(ascii: "H"):
self = .copyOutResponse
case UInt8(ascii: "W"):
self = .copyBothResponse
case UInt8(ascii: "D"):
self = .dataRow
case UInt8(ascii: "I"):
self = .emptyQueryResponse
case UInt8(ascii: "E"):
self = .error
case UInt8(ascii: "V"):
self = .functionCallResponse
case UInt8(ascii: "v"):
self = .negotiateProtocolVersion
case UInt8(ascii: "n"):
self = .noData
case UInt8(ascii: "N"):
self = .noticeResponse
case UInt8(ascii: "A"):
self = .notificationResponse
case UInt8(ascii: "t"):
self = .parameterDescription
case UInt8(ascii: "S"):
self = .parameterStatus
case UInt8(ascii: "1"):
self = .parseComplete
case UInt8(ascii: "s"):
self = .portalSuspended
case UInt8(ascii: "Z"):
self = .readyForQuery
case UInt8(ascii: "T"):
self = .rowDescription
default:
return nil
}
}
var rawValue: UInt8 {
switch self {
case .authentication:
return UInt8(ascii: "R")
case .backendKeyData:
return UInt8(ascii: "K")
case .bindComplete:
return UInt8(ascii: "2")
case .closeComplete:
return UInt8(ascii: "3")
case .commandComplete:
return UInt8(ascii: "C")
case .copyData:
return UInt8(ascii: "d")
case .copyDone:
return UInt8(ascii: "c")
case .copyInResponse:
return UInt8(ascii: "G")
case .copyOutResponse:
return UInt8(ascii: "H")
case .copyBothResponse:
return UInt8(ascii: "W")
case .dataRow:
return UInt8(ascii: "D")
case .emptyQueryResponse:
return UInt8(ascii: "I")
case .error:
return UInt8(ascii: "E")
case .functionCallResponse:
return UInt8(ascii: "V")
case .negotiateProtocolVersion:
return UInt8(ascii: "v")
case .noData:
return UInt8(ascii: "n")
case .noticeResponse:
return UInt8(ascii: "N")
case .notificationResponse:
return UInt8(ascii: "A")
case .parameterDescription:
return UInt8(ascii: "t")
case .parameterStatus:
return UInt8(ascii: "S")
case .parseComplete:
return UInt8(ascii: "1")
case .portalSuspended:
return UInt8(ascii: "s")
case .readyForQuery:
return UInt8(ascii: "Z")
case .rowDescription:
return UInt8(ascii: "T")
}
}
}
}
extension PostgresBackendMessage {
static func decode(from buffer: inout ByteBuffer, for messageID: ID) throws -> PostgresBackendMessage {
switch messageID {
case .authentication:
return try .authentication(.decode(from: &buffer))
case .backendKeyData:
return try .backendKeyData(.decode(from: &buffer))
case .bindComplete:
return .bindComplete
case .closeComplete:
return .closeComplete
case .commandComplete:
guard let commandTag = buffer.readNullTerminatedString() else {
throw PSQLPartialDecodingError.fieldNotDecodable(type: String.self)
}
return .commandComplete(commandTag)
case .dataRow:
return try .dataRow(.decode(from: &buffer))
case .emptyQueryResponse:
return .emptyQueryResponse
case .parameterStatus:
return try .parameterStatus(.decode(from: &buffer))
case .error:
return try .error(.decode(from: &buffer))
case .noData:
return .noData
case .noticeResponse:
return try .notice(.decode(from: &buffer))
case .notificationResponse:
return try .notification(.decode(from: &buffer))
case .parameterDescription:
return try .parameterDescription(.decode(from: &buffer))
case .parseComplete:
return .parseComplete
case .portalSuspended:
return .portalSuspended
case .readyForQuery:
return try .readyForQuery(.decode(from: &buffer))
case .rowDescription:
return try .rowDescription(.decode(from: &buffer))
case .copyData, .copyDone, .copyInResponse, .copyOutResponse, .copyBothResponse, .functionCallResponse, .negotiateProtocolVersion:
preconditionFailure()
}
}
}
extension PostgresBackendMessage: CustomDebugStringConvertible {
var debugDescription: String {
switch self {
case .authentication(let authentication):
return ".authentication(\(String(reflecting: authentication)))"
case .backendKeyData(let backendKeyData):
return ".backendKeyData(\(String(reflecting: backendKeyData)))"
case .bindComplete:
return ".bindComplete"
case .closeComplete:
return ".closeComplete"
case .commandComplete(let commandTag):
return ".commandComplete(\(String(reflecting: commandTag)))"
case .dataRow(let dataRow):
return ".dataRow(\(String(reflecting: dataRow)))"
case .emptyQueryResponse:
return ".emptyQueryResponse"
case .error(let error):
return ".error(\(String(reflecting: error)))"
case .noData:
return ".noData"
case .notice(let notice):
return ".notice(\(String(reflecting: notice)))"
case .notification(let notification):
return ".notification(\(String(reflecting: notification)))"
case .parameterDescription(let parameterDescription):
return ".parameterDescription(\(String(reflecting: parameterDescription)))"
case .parameterStatus(let parameterStatus):
return ".parameterStatus(\(String(reflecting: parameterStatus)))"
case .parseComplete:
return ".parseComplete"
case .portalSuspended:
return ".portalSuspended"
case .readyForQuery(let transactionState):
return ".readyForQuery(\(String(reflecting: transactionState)))"
case .rowDescription(let rowDescription):
return ".rowDescription(\(String(reflecting: rowDescription)))"
case .sslSupported:
return ".sslSupported"
case .sslUnsupported:
return ".sslUnsupported"
}
}
}