1use pg_proto_fsm::protocol;
8
9protocol! {
10 pub mod frontend {
11 initial Ready;
12 messages {
13 internal: crate::codec::FrontendMessage,
14 external: crate::codec::BackendMessage,
15 }
16 Ready internal {
17 Query(query: bytes::Bytes) => Simple [Dirty] <= crate::codec::FrontendMessage::Query(_),
18 BeginExtended(begin_extended) => Building,
19 FunctionCall(function_call: crate::codec::FunctionCall) => FunctionCalling [Dirty] <= crate::codec::FrontendMessage::FunctionCall(_),
20 Reset(reset) => Resetting [Dirty],
21 Terminate(terminate) => Terminated <= crate::codec::FrontendMessage::Terminate,
22 }
23 FunctionCalling external {
24 FunctionResponse(function_response: bytes::Bytes) => AwaitingReady <= crate::codec::BackendMessage::FunctionCallResponse(_),
25 Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
26 }
27 Simple external {
28 Continue(continue_response: crate::codec::BackendMessage) => Simple <= crate::codec::BackendMessage::RowDescription(_)
29 | crate::codec::BackendMessage::DataRow(_)
30 | crate::codec::BackendMessage::CommandComplete(_)
31 | crate::codec::BackendMessage::EmptyQueryResponse,
32 CopyIn(enter_copy_in: crate::codec::CopyResponse) => CopyIn <= crate::codec::BackendMessage::CopyInResponse(_),
33 CopyOut(enter_copy_out: crate::codec::CopyResponse) => CopyOut <= crate::codec::BackendMessage::CopyOutResponse(_),
34 CopyBoth(enter_copy_both: crate::codec::CopyResponse) => CopyBoth <= crate::codec::BackendMessage::CopyBothResponse(_),
35 Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
36 Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
37 }
38 Building internal {
39 Parse(parse: crate::codec::Parse) => Building [Dirty] <= crate::codec::FrontendMessage::Parse(_),
40 Describe(describe: crate::codec::Describe) => Building <= crate::codec::FrontendMessage::Describe(_),
41 Bind(bind: crate::codec::Bind) => BoundBuilding [Dirty] <= crate::codec::FrontendMessage::Bind(_),
42 Close(close: crate::codec::Close) => Building <= crate::codec::FrontendMessage::Close(_),
43 Flush(flush) => Building <= crate::codec::FrontendMessage::Flush,
44 Sync(sync) => AwaitingReady <= crate::codec::FrontendMessage::Sync,
45 }
46 BoundBuilding internal {
47 Parse(parse: crate::codec::Parse) => BoundBuilding [Dirty] <= crate::codec::FrontendMessage::Parse(_),
48 Describe(describe: crate::codec::Describe) => BoundBuilding <= crate::codec::FrontendMessage::Describe(_),
49 Bind(bind: crate::codec::Bind) => BoundBuilding [Dirty] <= crate::codec::FrontendMessage::Bind(_),
50 Execute(execute: crate::codec::Execute) => BoundBuilding <= crate::codec::FrontendMessage::Execute(_),
51 Close(close: crate::codec::Close) => BoundBuilding <= crate::codec::FrontendMessage::Close(_),
52 Flush(flush) => BoundBuilding <= crate::codec::FrontendMessage::Flush,
53 Sync(sync) => AwaitingReady <= crate::codec::FrontendMessage::Sync,
54 }
55 AwaitingReady external {
56 Continue(continue_response: crate::codec::BackendMessage) => AwaitingReady <= crate::codec::BackendMessage::ParseComplete
57 | crate::codec::BackendMessage::BindComplete
58 | crate::codec::BackendMessage::CloseComplete
59 | crate::codec::BackendMessage::RowDescription(_)
60 | crate::codec::BackendMessage::NoData
61 | crate::codec::BackendMessage::ParameterDescription(_)
62 | crate::codec::BackendMessage::DataRow(_)
63 | crate::codec::BackendMessage::CommandComplete(_)
64 | crate::codec::BackendMessage::PortalSuspended
65 | crate::codec::BackendMessage::EmptyQueryResponse,
66 Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
67 Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
68 }
69 CopyIn mixed {
70 internal CopyData(copy_data: bytes::Bytes) => CopyIn <= crate::codec::FrontendMessage::CopyData(_),
71 internal CopyDone(copy_done) => AwaitingReady <= crate::codec::FrontendMessage::CopyDone,
72 internal CopyFail(copy_fail: bytes::Bytes) => AwaitingReady <= crate::codec::FrontendMessage::CopyFail(_),
73 external Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
74 }
75 CopyOut external {
76 CopyData(copy_data: bytes::Bytes) => CopyOut <= crate::codec::BackendMessage::CopyData(_),
77 CopyDone(copy_done) => AwaitingReady <= crate::codec::BackendMessage::CopyDone,
78 Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
79 }
80 CopyBoth mixed {
81 internal SendCopyData(send_copy_data: bytes::Bytes) => CopyBoth <= crate::codec::FrontendMessage::CopyData(_),
82 external ReceiveCopyData(receive_copy_data: bytes::Bytes) => CopyBoth <= crate::codec::BackendMessage::CopyData(_),
83 internal SendCopyDone(send_copy_done) => CopyBothClientDone <= crate::codec::FrontendMessage::CopyDone,
84 external ReceiveCopyDone(receive_copy_done) => CopyBothServerDone <= crate::codec::BackendMessage::CopyDone,
85 external Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
86 }
87 CopyBothClientDone external {
88 ReceiveCopyData(receive_copy_data: bytes::Bytes) => CopyBothClientDone <= crate::codec::BackendMessage::CopyData(_),
89 ReceiveCopyDone(receive_copy_done) => AwaitingReady <= crate::codec::BackendMessage::CopyDone,
90 Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
91 }
92 CopyBothServerDone internal {
93 SendCopyData(send_copy_data: bytes::Bytes) => CopyBothServerDone <= crate::codec::FrontendMessage::CopyData(_),
94 SendCopyDone(send_copy_done) => AwaitingReady <= crate::codec::FrontendMessage::CopyDone,
95 }
96 Draining external {
97 Continue(continue_response: crate::codec::BackendMessage) => Draining <= crate::codec::BackendMessage::RowDescription(_)
98 | crate::codec::BackendMessage::DataRow(_)
99 | crate::codec::BackendMessage::CommandComplete(_)
100 | crate::codec::BackendMessage::EmptyQueryResponse
101 | crate::codec::BackendMessage::ParseComplete
102 | crate::codec::BackendMessage::BindComplete
103 | crate::codec::BackendMessage::CloseComplete
104 | crate::codec::BackendMessage::NoData
105 | crate::codec::BackendMessage::ParameterDescription(_)
106 | crate::codec::BackendMessage::PortalSuspended,
107 Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
108 }
109 Resetting external {
110 Continue(continue_reset: crate::codec::BackendMessage) => Resetting <= crate::codec::BackendMessage::RowDescription(_)
111 | crate::codec::BackendMessage::DataRow(_)
112 | crate::codec::BackendMessage::EmptyQueryResponse,
113 DiscardComplete(discard_complete: bytes::Bytes) => ResetComplete <= crate::codec::BackendMessage::CommandComplete(_),
114 Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
115 }
116 ResetComplete external {
117 Continue(continue_reset: crate::codec::BackendMessage) => ResetComplete <= crate::codec::BackendMessage::RowDescription(_)
118 | crate::codec::BackendMessage::DataRow(_)
119 | crate::codec::BackendMessage::CommandComplete(_)
120 | crate::codec::BackendMessage::EmptyQueryResponse,
121 ReadyClean(ready_clean: crate::codec::TransactionStatus) => Ready [Pristine] <= crate::codec::BackendMessage::ReadyForQuery(crate::codec::TransactionStatus::Idle),
122 ReadyDirty(ready_dirty: crate::codec::TransactionStatus) => Ready [Dirty] <= crate::codec::BackendMessage::ReadyForQuery(crate::codec::TransactionStatus::InTransaction | crate::codec::TransactionStatus::FailedTransaction),
123 Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
124 }
125 Terminated external {}
126 }
127}
128
129protocol! {
130 pub mod pre_startup {
131 initial PreStartup;
132 messages {
133 internal: crate::pre_startup::PreStartupMessage,
134 external: crate::pre_startup::EncryptionReply,
135 }
136 PreStartup internal {
137 SslRequest(ssl_request) => AwaitingSslReply <= crate::pre_startup::PreStartupMessage::SslRequest,
138 GssRequest(gss_request) => AwaitingGssReply <= crate::pre_startup::PreStartupMessage::GssEncRequest,
139 Cancel(cancel: (u32, bytes::Bytes)) => Terminated <= crate::pre_startup::PreStartupMessage::CancelRequest { .. },
140 Startup(startup: crate::startup::StartupMessage) => Auth <= crate::pre_startup::PreStartupMessage::Startup(_),
141 }
142 AwaitingSslReply external {
143 Accept(accept) => TlsHandshake <= crate::pre_startup::EncryptionReply::Accepted,
144 Reject(reject) => PreStartup <= crate::pre_startup::EncryptionReply::Rejected,
145 LegacyError(legacy_error) => Terminated <= crate::pre_startup::EncryptionReply::LegacyError,
146 }
147 AwaitingGssReply external {
148 Accept(accept) => GssHandshake <= crate::pre_startup::EncryptionReply::Accepted,
149 Reject(reject) => PreStartup <= crate::pre_startup::EncryptionReply::Rejected,
150 LegacyError(legacy_error) => Terminated <= crate::pre_startup::EncryptionReply::LegacyError,
151 }
152 TlsHandshake internal {
153 HandshakeComplete(complete) => PreStartup,
154 }
155 GssHandshake internal {
156 HandshakeComplete(complete) => PreStartup,
157 }
158 Auth external {}
159 Terminated external {}
160 }
161}
162
163protocol! {
164 pub mod server_pre_startup {
165 initial PreStartup;
166 messages {
167 internal: crate::pre_startup::EncryptionReply,
168 external: crate::pre_startup::PreStartupMessage,
169 }
170 PreStartup external {
171 SslRequest(ssl_request) => SslDecision <= crate::pre_startup::PreStartupMessage::SslRequest,
172 GssRequest(gss_request) => GssDecision <= crate::pre_startup::PreStartupMessage::GssEncRequest,
173 Cancel(cancel: (u32, bytes::Bytes)) => Terminated <= crate::pre_startup::PreStartupMessage::CancelRequest { .. },
174 Startup(startup: crate::startup::StartupMessage) => Auth <= crate::pre_startup::PreStartupMessage::Startup(_),
175 }
176 SslDecision internal {
177 Accept(accept) => TlsHandshake <= crate::pre_startup::EncryptionReply::Accepted,
178 Reject(reject) => PreStartup <= crate::pre_startup::EncryptionReply::Rejected,
179 LegacyError(legacy_error) => Terminated <= crate::pre_startup::EncryptionReply::LegacyError,
180 }
181 GssDecision internal {
182 Accept(accept) => GssHandshake <= crate::pre_startup::EncryptionReply::Accepted,
183 Reject(reject) => PreStartup <= crate::pre_startup::EncryptionReply::Rejected,
184 LegacyError(legacy_error) => Terminated <= crate::pre_startup::EncryptionReply::LegacyError,
185 }
186 TlsHandshake internal {
187 HandshakeComplete(complete) => PreStartup,
188 }
189 GssHandshake internal {
190 HandshakeComplete(complete) => PreStartup,
191 }
192 Auth internal {}
193 Terminated internal {}
194 }
195}
196
197protocol! {
198 pub mod authentication {
199 initial Auth;
200 messages {
201 internal: crate::codec::FrontendMessage,
202 external: crate::codec::BackendMessage,
203 }
204 Auth external {
205 Negotiate(negotiate: crate::codec::NegotiateProtocolVersion) => Auth <= crate::codec::BackendMessage::NegotiateProtocolVersion(_),
206 Ok(ok) => AwaitingStartupReady <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Ok),
207 Cleartext(cleartext) => PasswordResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::CleartextPassword),
208 Md5(md5: [u8; 4]) => PasswordResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Md5Password { .. }),
209 Sasl(sasl: Vec<bytes::Bytes>) => SaslInitial <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Sasl { .. }),
210 Gss(gss) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Gss),
211 Sspi(sspi) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Sspi),
212 KerberosV5(kerberos_v5) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::KerberosV5),
213 Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
214 }
215 PasswordResponse internal {
216 Password(password: bytes::Bytes) => AwaitingAuthOk <= crate::codec::FrontendMessage::PasswordResponse(_),
217 }
218 TokenResponse internal {
219 Response(response: bytes::Bytes) => TokenChallenge <= crate::codec::FrontendMessage::PasswordResponse(_),
220 }
221 TokenChallenge external {
222 Continue(continue_token: bytes::Bytes) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::GssContinue(_)),
223 Ok(ok) => AwaitingStartupReady <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Ok),
224 Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
225 }
226 SaslInitial internal {
227 Initial(initial: crate::server_auth::SaslInitialResponse) => Sasl <= crate::codec::FrontendMessage::PasswordResponse(_),
228 }
229 Sasl external {
230 Continue(continue_response: bytes::Bytes) => SaslChallenge <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::SaslContinue(_)),
231 Final(final_response: bytes::Bytes) => SaslFinal <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::SaslFinal(_)),
232 Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
233 }
234 SaslChallenge internal {
235 Response(response: bytes::Bytes) => Sasl <= crate::codec::FrontendMessage::PasswordResponse(_),
236 }
237 SaslFinal internal {
238 Verified(verified) => AwaitingAuthOk,
239 }
240 AwaitingAuthOk external {
241 Ok(ok) => AwaitingStartupReady <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Ok),
242 Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
243 }
244 AwaitingStartupReady external {
245 Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
246 }
247 Ready external {}
248 Terminated external {}
249 }
250}
251
252protocol! {
253 pub mod backend {
254 initial Ready;
255 messages {
256 internal: crate::codec::BackendMessage,
257 external: crate::codec::FrontendMessage,
258 }
259 Ready external {
260 Query(query: bytes::Bytes) => Simple [Dirty] <= crate::codec::FrontendMessage::Query(_),
261 Parse(parse: crate::codec::Parse) => ParseResponse [Dirty] <= crate::codec::FrontendMessage::Parse(_),
262 Bind(bind: crate::codec::Bind) => BindResponse [Dirty] <= crate::codec::FrontendMessage::Bind(_),
263 Describe(describe: crate::codec::Describe) => DescribeResponse <= crate::codec::FrontendMessage::Describe(_),
264 Execute(execute: crate::codec::Execute) => ExecuteResponse [Dirty] <= crate::codec::FrontendMessage::Execute(_),
265 Close(close: crate::codec::Close) => CloseResponse <= crate::codec::FrontendMessage::Close(_),
266 FunctionCall(function_call: crate::codec::FunctionCall) => FunctionResponse [Dirty] <= crate::codec::FrontendMessage::FunctionCall(_),
267 Terminate(terminate) => Terminated <= crate::codec::FrontendMessage::Terminate,
268 }
269 Simple internal {
270 Continue(continue_response: crate::codec::BackendMessage) => Simple <= crate::codec::BackendMessage::RowDescription(_)
271 | crate::codec::BackendMessage::DataRow(_)
272 | crate::codec::BackendMessage::CommandComplete(_)
273 | crate::codec::BackendMessage::EmptyQueryResponse,
274 CopyIn(copy_in: crate::codec::CopyResponse) => SimpleCopyIn <= crate::codec::BackendMessage::CopyInResponse(_),
275 CopyOut(copy_out: crate::codec::CopyResponse) => SimpleCopyOut <= crate::codec::BackendMessage::CopyOutResponse(_),
276 CopyBoth(copy_both: crate::codec::CopyResponse) => SimpleCopyBoth <= crate::codec::BackendMessage::CopyBothResponse(_),
277 Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
278 Error(error: crate::codec::DiagnosticResponse) => SimpleError <= crate::codec::BackendMessage::ErrorResponse(_),
279 }
280 SimpleError internal {
281 Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
282 }
283 Building external {
284 Parse(parse: crate::codec::Parse) => ParseResponse [Dirty] <= crate::codec::FrontendMessage::Parse(_),
285 Bind(bind: crate::codec::Bind) => BindResponse [Dirty] <= crate::codec::FrontendMessage::Bind(_),
286 Describe(describe: crate::codec::Describe) => DescribeResponse <= crate::codec::FrontendMessage::Describe(_),
287 Execute(execute: crate::codec::Execute) => ExecuteResponse [Dirty] <= crate::codec::FrontendMessage::Execute(_),
288 Close(close: crate::codec::Close) => CloseResponse <= crate::codec::FrontendMessage::Close(_),
289 Flush(flush) => Building <= crate::codec::FrontendMessage::Flush,
290 Sync(sync) => SyncResponse <= crate::codec::FrontendMessage::Sync,
291 }
292 ParseResponse internal {
293 Complete(complete) => Building <= crate::codec::BackendMessage::ParseComplete,
294 Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
295 }
296 BindResponse internal {
297 Complete(complete) => Building <= crate::codec::BackendMessage::BindComplete,
298 Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
299 }
300 DescribeResponse internal {
301 ParameterDescription(parameter_description: Vec<u32>) => DescribeResponse <= crate::codec::BackendMessage::ParameterDescription(_),
302 RowDescription(row_description: crate::codec::RowDescription) => Building <= crate::codec::BackendMessage::RowDescription(_),
303 NoData(no_data) => Building <= crate::codec::BackendMessage::NoData,
304 Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
305 }
306 ExecuteResponse internal {
307 Continue(continue_response: crate::codec::BackendMessage) => ExecuteResponse <= crate::codec::BackendMessage::RowDescription(_)
308 | crate::codec::BackendMessage::DataRow(_)
309 | crate::codec::BackendMessage::EmptyQueryResponse,
310 CopyIn(copy_in: crate::codec::CopyResponse) => ExtendedCopyIn <= crate::codec::BackendMessage::CopyInResponse(_),
311 CopyOut(copy_out: crate::codec::CopyResponse) => ExtendedCopyOut <= crate::codec::BackendMessage::CopyOutResponse(_),
312 CopyBoth(copy_both: crate::codec::CopyResponse) => ExtendedCopyBoth <= crate::codec::BackendMessage::CopyBothResponse(_),
313 CommandComplete(command_complete: bytes::Bytes) => Building <= crate::codec::BackendMessage::CommandComplete(_),
314 PortalSuspended(portal_suspended) => Building <= crate::codec::BackendMessage::PortalSuspended,
315 Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
316 }
317 CloseResponse internal {
318 Complete(complete) => Building <= crate::codec::BackendMessage::CloseComplete,
319 Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
320 }
321 ExtendedError external {
322 Discard(discard) => ExtendedError <= crate::codec::FrontendMessage::Parse(_)
323 | crate::codec::FrontendMessage::Bind(_)
324 | crate::codec::FrontendMessage::Describe(_)
325 | crate::codec::FrontendMessage::Execute(_)
326 | crate::codec::FrontendMessage::Close(_)
327 | crate::codec::FrontendMessage::Flush
328 | crate::codec::FrontendMessage::Query(_)
329 | crate::codec::FrontendMessage::FunctionCall(_)
330 | crate::codec::FrontendMessage::Terminate
331 | crate::codec::FrontendMessage::CopyData(_)
332 | crate::codec::FrontendMessage::CopyDone
333 | crate::codec::FrontendMessage::CopyFail(_)
334 | crate::codec::FrontendMessage::PasswordResponse(_),
335 Sync(sync) => SyncResponse <= crate::codec::FrontendMessage::Sync,
336 }
337 SyncResponse internal {
338 Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
339 }
340 FunctionResponse internal {
341 Result(result: bytes::Bytes) => FunctionReady <= crate::codec::BackendMessage::FunctionCallResponse(_),
342 Error(error: crate::codec::DiagnosticResponse) => FunctionReady <= crate::codec::BackendMessage::ErrorResponse(_),
343 }
344 FunctionReady internal {
345 Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
346 }
347 SimpleCopyIn external {
348 Data(data: bytes::Bytes) => SimpleCopyIn <= crate::codec::FrontendMessage::CopyData(_),
349 Done(done) => SimpleCopyInDone <= crate::codec::FrontendMessage::CopyDone,
350 Fail(fail: bytes::Bytes) => SimpleCopyInFailed <= crate::codec::FrontendMessage::CopyFail(_),
351 }
352 SimpleCopyInDone internal {
353 CommandComplete(command_complete: bytes::Bytes) => SimpleCopyReady <= crate::codec::BackendMessage::CommandComplete(_),
354 }
355 SimpleCopyInFailed internal {
356 Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
357 }
358 SimpleCopyOut internal {
359 Data(data: bytes::Bytes) => SimpleCopyOut <= crate::codec::BackendMessage::CopyData(_),
360 Done(done) => SimpleCopyOutDone <= crate::codec::BackendMessage::CopyDone,
361 Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
362 }
363 SimpleCopyOutDone internal {
364 CommandComplete(command_complete: bytes::Bytes) => SimpleCopyReady <= crate::codec::BackendMessage::CommandComplete(_),
365 }
366 SimpleCopyReady internal {
367 Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
368 }
369 ExtendedCopyIn external {
370 Data(data: bytes::Bytes) => ExtendedCopyIn <= crate::codec::FrontendMessage::CopyData(_),
371 Done(done) => ExtendedCopyInDone <= crate::codec::FrontendMessage::CopyDone,
372 Fail(fail: bytes::Bytes) => ExtendedCopyInFailed <= crate::codec::FrontendMessage::CopyFail(_),
373 }
374 ExtendedCopyInDone internal {
375 CommandComplete(command_complete: bytes::Bytes) => Building <= crate::codec::BackendMessage::CommandComplete(_),
376 }
377 ExtendedCopyInFailed internal {
378 Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
379 }
380 ExtendedCopyOut internal {
381 Data(data: bytes::Bytes) => ExtendedCopyOut <= crate::codec::BackendMessage::CopyData(_),
382 Done(done) => ExtendedCopyOutDone <= crate::codec::BackendMessage::CopyDone,
383 Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
384 }
385 ExtendedCopyOutDone internal {
386 CommandComplete(command_complete: bytes::Bytes) => Building <= crate::codec::BackendMessage::CommandComplete(_),
387 }
388 SimpleCopyBoth mixed {
389 internal SendData(send_data: bytes::Bytes) => SimpleCopyBoth <= crate::codec::BackendMessage::CopyData(_),
390 external ReceiveData(receive_data: bytes::Bytes) => SimpleCopyBoth <= crate::codec::FrontendMessage::CopyData(_),
391 internal SendDone(send_done) => SimpleCopyBothServerDone <= crate::codec::BackendMessage::CopyDone,
392 external ReceiveDone(receive_done) => SimpleCopyBothClientDone <= crate::codec::FrontendMessage::CopyDone,
393 external Fail(fail: bytes::Bytes) => SimpleCopyBothFailed <= crate::codec::FrontendMessage::CopyFail(_),
394 internal Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
395 }
396 SimpleCopyBothClientDone internal {
397 SendData(send_data: bytes::Bytes) => SimpleCopyBothClientDone <= crate::codec::BackendMessage::CopyData(_),
398 SendDone(send_done) => SimpleCopyBothDone <= crate::codec::BackendMessage::CopyDone,
399 Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
400 }
401 SimpleCopyBothServerDone external {
402 ReceiveData(receive_data: bytes::Bytes) => SimpleCopyBothServerDone <= crate::codec::FrontendMessage::CopyData(_),
403 ReceiveDone(receive_done) => SimpleCopyBothDone <= crate::codec::FrontendMessage::CopyDone,
404 Fail(fail: bytes::Bytes) => SimpleCopyBothFailed <= crate::codec::FrontendMessage::CopyFail(_),
405 }
406 SimpleCopyBothDone internal {
407 CommandComplete(command_complete: bytes::Bytes) => SimpleCopyReady <= crate::codec::BackendMessage::CommandComplete(_),
408 }
409 SimpleCopyBothFailed internal {
410 Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
411 }
412 ExtendedCopyBoth mixed {
413 internal SendData(send_data: bytes::Bytes) => ExtendedCopyBoth <= crate::codec::BackendMessage::CopyData(_),
414 external ReceiveData(receive_data: bytes::Bytes) => ExtendedCopyBoth <= crate::codec::FrontendMessage::CopyData(_),
415 internal SendDone(send_done) => ExtendedCopyBothServerDone <= crate::codec::BackendMessage::CopyDone,
416 external ReceiveDone(receive_done) => ExtendedCopyBothClientDone <= crate::codec::FrontendMessage::CopyDone,
417 external Fail(fail: bytes::Bytes) => ExtendedCopyBothFailed <= crate::codec::FrontendMessage::CopyFail(_),
418 internal Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
419 }
420 ExtendedCopyBothClientDone internal {
421 SendData(send_data: bytes::Bytes) => ExtendedCopyBothClientDone <= crate::codec::BackendMessage::CopyData(_),
422 SendDone(send_done) => ExtendedCopyBothDone <= crate::codec::BackendMessage::CopyDone,
423 Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
424 }
425 ExtendedCopyBothServerDone external {
426 ReceiveData(receive_data: bytes::Bytes) => ExtendedCopyBothServerDone <= crate::codec::FrontendMessage::CopyData(_),
427 ReceiveDone(receive_done) => ExtendedCopyBothDone <= crate::codec::FrontendMessage::CopyDone,
428 Fail(fail: bytes::Bytes) => ExtendedCopyBothFailed <= crate::codec::FrontendMessage::CopyFail(_),
429 }
430 ExtendedCopyBothDone internal {
431 CommandComplete(command_complete: bytes::Bytes) => Building <= crate::codec::BackendMessage::CommandComplete(_),
432 }
433 ExtendedCopyBothFailed internal {
434 Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
435 }
436 Terminated external {}
437 }
438}
439
440protocol! {
441 pub mod server_authentication {
442 initial Startup;
443 messages {
444 internal: crate::codec::BackendMessage,
445 external: crate::codec::FrontendMessage,
446 }
447 Startup internal {
448 Begin(begin) => Auth,
449 Reject(reject: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
450 }
451 Auth internal {
452 Negotiate(negotiate: crate::codec::NegotiateProtocolVersion) => Auth <= crate::codec::BackendMessage::NegotiateProtocolVersion(_),
453 Cleartext(cleartext) => PasswordResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::CleartextPassword),
454 Md5(md5: [u8; 4]) => PasswordResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Md5Password { .. }),
455 Sasl(sasl: Vec<bytes::Bytes>) => SaslInitial <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Sasl { .. }),
456 Gss(gss) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Gss),
457 Sspi(sspi) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Sspi),
458 KerberosV5(kerberos_v5) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::KerberosV5),
459 Ok(ok) => StartupReady <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Ok),
460 Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
461 }
462 PasswordResponse external {
463 Response(response: bytes::Bytes) => Auth <= crate::codec::FrontendMessage::PasswordResponse(_),
464 }
465 SaslInitial external {
466 Initial(initial: crate::server_auth::SaslInitialResponse) => Sasl <= crate::codec::FrontendMessage::PasswordResponse(_),
467 }
468 Sasl internal {
469 Continue(continue_response: bytes::Bytes) => SaslResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::SaslContinue(_)),
470 Final(final_response: bytes::Bytes) => Auth <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::SaslFinal(_)),
471 Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
472 }
473 SaslResponse external {
474 Response(response: bytes::Bytes) => Sasl <= crate::codec::FrontendMessage::PasswordResponse(_),
475 }
476 TokenResponse external {
477 Response(response: bytes::Bytes) => TokenPolicy <= crate::codec::FrontendMessage::PasswordResponse(_),
478 }
479 TokenPolicy internal {
480 Continue(continue_token: bytes::Bytes) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::GssContinue(_)),
481 Verified(verified) => Auth,
482 Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
483 }
484 StartupReady internal {
485 ParameterStatus(parameter_status: (bytes::Bytes, bytes::Bytes)) => StartupReady <= crate::codec::BackendMessage::ParameterStatus { .. },
486 BackendKeyData(backend_key_data: (u32, bytes::Bytes)) => StartupReady <= crate::codec::BackendMessage::BackendKeyData { .. },
487 Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
488 }
489 Ready external {}
490 Terminated external {}
491 }
492}
493
494#[cfg(test)]
495mod tests {
496 use std::collections::BTreeMap;
497
498 use bytes::Bytes;
499
500 use super::{
501 authentication, backend, frontend, pre_startup, server_authentication, server_pre_startup,
502 };
503 use crate::{
504 Conn,
505 auth::AuthOffer,
506 codec::{
507 Authentication, BackendMessage, Bind, Execute, FrontendMessage, Parse,
508 TransactionStatus,
509 },
510 demux::SessionItem,
511 session::{AwaitingReadyTransition, ReadyState},
512 startup::{ProtocolVersion, StartupMessage},
513 };
514 use frontend::{Event, RuntimeFsm, RuntimeState, Session};
515
516 #[test]
517 fn railroad_labels_use_variant_syntax_and_link_payload_types() {
518 let svg = frontend::FRONTEND_RAILROAD_SVG;
519 assert!(svg.contains("◁ ReceiveCopyData(</tspan>"));
520 assert!(svg.contains("bytes::Bytes</tspan>"));
521 assert!(svg.contains(")</tspan>"));
522 assert!(svg.contains("xlink:href=\"https://docs.rs/bytes/1/bytes/struct.Bytes.html\""));
523 assert!(svg.contains("class=\"link\""));
524 assert!(svg.contains("▷ Query(</tspan>"));
525 assert!(svg.contains(") [Dirty]</tspan>"));
526 assert!(!svg.contains("class=\"link\"> <g class=\"terminal\""));
527 assert!(!svg.contains("& ReceiveCopyData"));
528 }
529
530 macro_rules! exhaust_generated_runtime {
531 ($module:ident, $depth:expr) => {{
532 fn visit(runtime: $module::RuntimeFsm, depth: usize) {
533 for &event in $module::ALL_EVENTS {
534 let state = runtime.state();
535 let mut next = runtime;
536 match next.step(event) {
537 Ok(()) if depth > 0 => visit(next, depth - 1),
538 Ok(()) => {}
539 Err(error) => {
540 assert_eq!(error.state, state);
541 assert_eq!(error.event, event);
542 assert_eq!(next.state(), state);
543 }
544 }
545 }
546 }
547 visit($module::RuntimeFsm::new(), $depth);
548 }};
549 }
550
551 #[test]
552 fn every_generated_protocol_exhausts_reachable_valid_and_invalid_events() {
553 exhaust_generated_runtime!(frontend, 5);
554 exhaust_generated_runtime!(backend, 5);
555 exhaust_generated_runtime!(pre_startup, 5);
556 exhaust_generated_runtime!(server_pre_startup, 5);
557 exhaust_generated_runtime!(authentication, 6);
558 exhaust_generated_runtime!(server_authentication, 6);
559 }
560
561 #[test]
562 fn generated_backend_projection_is_state_aware() {
563 let parse = FrontendMessage::Parse(Parse {
564 statement: Bytes::from_static(b"statement"),
565 query: Bytes::from_static(b"select 1"),
566 parameter_types: Vec::new(),
567 });
568 assert_eq!(
569 backend::project_external(backend::RuntimeState::Ready, &parse),
570 Some(backend::Event::Parse)
571 );
572 assert_eq!(
573 backend::project_external(backend::RuntimeState::SimpleCopyIn, &parse),
574 None
575 );
576 assert_eq!(
577 backend::project_external(backend::RuntimeState::ExtendedError, &FrontendMessage::Sync,),
578 Some(backend::Event::Sync)
579 );
580 assert_eq!(
581 backend::project_external(
582 backend::RuntimeState::ExtendedError,
583 &FrontendMessage::Flush,
584 ),
585 Some(backend::Event::Discard)
586 );
587 assert_eq!(
588 backend::project_internal(
589 backend::RuntimeState::Simple,
590 &BackendMessage::ReadyForQuery(TransactionStatus::Idle),
591 ),
592 Some(backend::Event::Ready)
593 );
594 assert_eq!(
595 backend::project_internal(
596 backend::RuntimeState::ExtendedCopyOut,
597 &BackendMessage::CopyDone,
598 ),
599 Some(backend::Event::Done)
600 );
601 }
602
603 #[test]
604 fn generated_frontend_projection_covers_wire_messages_only() {
605 assert_eq!(
606 frontend::project_internal(frontend::RuntimeState::Building, &FrontendMessage::Sync,),
607 Some(frontend::Event::Sync)
608 );
609 assert_eq!(
610 frontend::project_external(
611 frontend::RuntimeState::Simple,
612 &BackendMessage::ReadyForQuery(TransactionStatus::Idle),
613 ),
614 Some(frontend::Event::Ready)
615 );
616 assert_eq!(
617 frontend::project_external(
618 frontend::RuntimeState::ResetComplete,
619 &BackendMessage::ReadyForQuery(TransactionStatus::FailedTransaction),
620 ),
621 Some(frontend::Event::ReadyDirty)
622 );
623 assert_eq!(
624 frontend::project_external(
625 frontend::RuntimeState::CopyIn,
626 &BackendMessage::CopyData(Bytes::from_static(b"illegal direction")),
627 ),
628 None
629 );
630 }
631
632 #[test]
633 fn codec_messages_drive_generated_extended_and_copy_sequences() {
634 let parse = FrontendMessage::Parse(Parse {
635 statement: Bytes::new(),
636 query: Bytes::from_static(b"select $1"),
637 parameter_types: vec![23],
638 });
639 let mut extended = backend::RuntimeFsm::new();
640 extended
641 .step_projected(&parse, backend::project_external)
642 .unwrap();
643 extended
644 .step_projected(&BackendMessage::ParseComplete, backend::project_internal)
645 .unwrap();
646 extended
647 .step_projected(&FrontendMessage::Sync, backend::project_external)
648 .unwrap();
649 extended
650 .step_projected(
651 &BackendMessage::ReadyForQuery(TransactionStatus::Idle),
652 backend::project_internal,
653 )
654 .unwrap();
655 assert_eq!(extended.state(), backend::RuntimeState::Ready);
656
657 let mut copy = backend::RuntimeFsm::new();
658 copy.step_projected(
659 &FrontendMessage::Query(Bytes::from_static(b"copy t from stdin")),
660 backend::project_external,
661 )
662 .unwrap();
663 copy.step_projected(
664 &BackendMessage::CopyInResponse(crate::codec::CopyResponse {
665 overall_format: 0,
666 column_formats: vec![0],
667 }),
668 backend::project_internal,
669 )
670 .unwrap();
671 assert_eq!(copy.state(), backend::RuntimeState::SimpleCopyIn);
672 assert!(
673 copy.step_projected(
674 &FrontendMessage::Query(Bytes::from_static(b"select 1")),
675 backend::project_external,
676 )
677 .is_err()
678 );
679 assert_eq!(copy.state(), backend::RuntimeState::SimpleCopyIn);
680 }
681
682 #[test]
683 fn generated_typestate_and_runtime_accept_the_extended_loop() {
684 let _typed = Session::new()
685 .begin_extended()
686 .parse()
687 .bind()
688 .execute()
689 .sync()
690 .ready();
691
692 let mut runtime = RuntimeFsm::new();
693 for event in [
694 Event::BeginExtended,
695 Event::Parse,
696 Event::Bind,
697 Event::Execute,
698 Event::Sync,
699 Event::Ready,
700 ] {
701 runtime.step(event).unwrap();
702 }
703 assert_eq!(runtime.state(), RuntimeState::Ready);
704 }
705
706 #[test]
707 fn generated_backend_discards_failed_pipeline_until_sync() {
708 let _typed = backend::Session::new()
709 .parse()
710 .error()
711 .discard()
712 .discard()
713 .sync()
714 .ready()
715 .terminate();
716
717 let mut runtime = backend::RuntimeFsm::new();
718 for event in [
719 backend::Event::Parse,
720 backend::Event::Error,
721 backend::Event::Discard,
722 backend::Event::Discard,
723 backend::Event::Sync,
724 backend::Event::Ready,
725 backend::Event::Terminate,
726 ] {
727 runtime.step(event).unwrap();
728 }
729 assert_eq!(runtime.state(), backend::RuntimeState::Terminated);
730 }
731
732 #[test]
733 fn generated_backend_copy_resumes_its_enclosing_session() {
734 let _simple = backend::Session::new()
735 .query()
736 .copy_in()
737 .data()
738 .done()
739 .command_complete()
740 .ready();
741 let _extended = backend::Session::new()
742 .execute()
743 .copy_out()
744 .data()
745 .done()
746 .command_complete()
747 .sync()
748 .ready();
749
750 let mut runtime = backend::RuntimeFsm::new();
751 for event in [
752 backend::Event::Execute,
753 backend::Event::CopyOut,
754 backend::Event::Data,
755 backend::Event::Done,
756 backend::Event::CommandComplete,
757 ] {
758 runtime.step(event).unwrap();
759 }
760 assert_eq!(runtime.state(), backend::RuntimeState::Building);
761 runtime.step(backend::Event::Sync).unwrap();
762 runtime.step(backend::Event::Ready).unwrap();
763 assert_eq!(runtime.state(), backend::RuntimeState::Ready);
764 }
765
766 #[test]
767 fn generated_backend_copy_both_tracks_independent_half_closes() {
768 let _server_first = backend::Session::new()
769 .query()
770 .copy_both()
771 .send_data()
772 .receive_data()
773 .send_done()
774 .receive_data()
775 .receive_done()
776 .command_complete()
777 .ready();
778 let _client_first = backend::Session::new()
779 .execute()
780 .copy_both()
781 .receive_done()
782 .send_data()
783 .send_done()
784 .command_complete()
785 .sync()
786 .ready();
787
788 let mut runtime = backend::RuntimeFsm::new();
789 runtime.step(backend::Event::Query).unwrap();
790 runtime.step(backend::Event::CopyBoth).unwrap();
791 assert_eq!(runtime.choice(), backend::ChoiceKind::Mixed);
792 assert_eq!(
793 runtime.event_choice(backend::Event::SendData),
794 Some(backend::ChoiceKind::Internal)
795 );
796 assert_eq!(
797 runtime.event_choice(backend::Event::ReceiveData),
798 Some(backend::ChoiceKind::External)
799 );
800 runtime.step(backend::Event::SendDone).unwrap();
801 assert!(runtime.step(backend::Event::SendData).is_err());
802 runtime.step(backend::Event::ReceiveDone).unwrap();
803 runtime.step(backend::Event::CommandComplete).unwrap();
804 runtime.step(backend::Event::Ready).unwrap();
805 assert_eq!(runtime.state(), backend::RuntimeState::Ready);
806 }
807
808 #[test]
809 fn generated_runtime_rejects_query_during_copy() {
810 let mut runtime = RuntimeFsm::new();
811 runtime.step(Event::Query).unwrap();
812 runtime.step(Event::CopyIn).unwrap();
813 assert!(runtime.step(Event::Query).is_err());
814 assert_eq!(
815 runtime.event_choice(Event::Error),
816 Some(frontend::ChoiceKind::External)
817 );
818 runtime.step(Event::Error).unwrap();
819 assert_eq!(runtime.state(), RuntimeState::Draining);
820 }
821
822 #[test]
823 fn generated_copy_both_waits_for_both_half_closes() {
824 let mut directions = RuntimeFsm::new();
825 directions.step(Event::Query).unwrap();
826 directions.step(Event::CopyBoth).unwrap();
827 assert_eq!(directions.choice(), frontend::ChoiceKind::Mixed);
828 assert_eq!(
829 directions.event_choice(Event::SendCopyData),
830 Some(frontend::ChoiceKind::Internal)
831 );
832 assert_eq!(
833 directions.event_choice(Event::ReceiveCopyData),
834 Some(frontend::ChoiceKind::External)
835 );
836
837 let mut client_first = RuntimeFsm::new();
838 for event in [
839 Event::Query,
840 Event::CopyBoth,
841 Event::SendCopyDone,
842 Event::ReceiveCopyData,
843 Event::ReceiveCopyDone,
844 ] {
845 client_first.step(event).unwrap();
846 }
847 assert_eq!(client_first.state(), RuntimeState::AwaitingReady);
848
849 let mut server_first = RuntimeFsm::new();
850 for event in [
851 Event::Query,
852 Event::CopyBoth,
853 Event::ReceiveCopyDone,
854 Event::SendCopyData,
855 Event::SendCopyDone,
856 ] {
857 server_first.step(event).unwrap();
858 }
859 assert_eq!(server_first.state(), RuntimeState::AwaitingReady);
860 }
861
862 #[test]
863 fn generated_function_call_and_termination_match_typed_paths() {
864 let _function = Session::new()
865 .function_call()
866 .function_response()
867 .ready()
868 .terminate();
869
870 let mut runtime = RuntimeFsm::new();
871 for event in [
872 Event::FunctionCall,
873 Event::FunctionResponse,
874 Event::Ready,
875 Event::Terminate,
876 ] {
877 runtime.step(event).unwrap();
878 }
879 assert_eq!(runtime.state(), RuntimeState::Terminated);
880 }
881
882 #[test]
883 fn generated_pool_reset_requires_discard_and_ready_evidence() {
884 let _typed = Session::new()
885 .reset()
886 .continue_reset()
887 .discard_complete()
888 .continue_reset()
889 .ready_clean();
890
891 let mut runtime = RuntimeFsm::new();
892 runtime.step(Event::Reset).unwrap();
893 assert!(runtime.step(Event::ReadyClean).is_err());
894 runtime.step(Event::DiscardComplete).unwrap();
895 runtime.step(Event::ReadyClean).unwrap();
896 assert_eq!(runtime.state(), RuntimeState::Ready);
897
898 let dirty: Conn<(), crate::auth::Ready, crate::Dirty> =
899 Conn::new(()).transition().mark_dirty();
900 let (resetting, _) = dirty.begin_reset().unwrap();
901 let crate::session::ResettingTransition::Complete(complete) =
902 resetting.offer(SessionItem::CommandComplete {
903 tag: Bytes::from_static(b"DISCARD ALL"),
904 command: crate::demux::CommandIndex(1),
905 notices: Vec::new(),
906 })
907 else {
908 panic!("DISCARD ALL did not advance reset recovery")
909 };
910 let crate::session::ResetCompleteTransition::Ready(ready) =
911 complete.offer(SessionItem::ReadyForQuery {
912 status: TransactionStatus::Idle,
913 parameters_changed: false,
914 })
915 else {
916 panic!("idle readiness did not restore pristine evidence")
917 };
918 ready.release();
919 }
920
921 #[test]
922 fn generated_transport_session_tracks_cleanliness_effects() {
923 #[derive(Debug)]
924 struct InitiallyClean;
925
926 let ready: frontend::TypedSession<(), frontend::Ready, InitiallyClean> =
927 frontend::TypedSession::with_transport(());
928 let (dirty, query): (
929 frontend::TypedSession<(), frontend::Simple, frontend::Dirty>,
930 Bytes,
931 ) = ready
932 .query(Bytes::from_static(b"select 1"), |(), query| {
933 Ok::<_, std::convert::Infallible>(query)
934 })
935 .expect("query handler is infallible");
936 assert_eq!(query, Bytes::from_static(b"select 1"));
937 let (dirty, _status): (
938 frontend::TypedSession<(), frontend::Ready, frontend::Dirty>,
939 TransactionStatus,
940 ) = dirty
941 .ready(TransactionStatus::Idle, |(), status| {
942 Ok::<_, std::convert::Infallible>(status)
943 })
944 .expect("readiness handler is infallible");
945 assert_eq!(dirty.into_transport(), ());
946
947 let dirty: frontend::TypedSession<(), frontend::Ready, frontend::Dirty> =
948 frontend::TypedSession::with_transport(());
949 let (reset_complete, _tag) = dirty
950 .reset()
951 .discard_complete(Bytes::from_static(b"DISCARD ALL"), |(), tag| {
952 Ok::<_, std::convert::Infallible>(tag)
953 })
954 .expect("command handler is infallible");
955 let (clean, _status): (
956 frontend::TypedSession<(), frontend::Ready, frontend::Pristine>,
957 TransactionStatus,
958 ) = reset_complete
959 .ready_clean(TransactionStatus::Idle, |(), status| {
960 Ok::<_, std::convert::Infallible>(status)
961 })
962 .expect("readiness handler is infallible");
963 assert_eq!(clean.into_transport(), ());
964 }
965
966 #[test]
967 fn generated_frontend_parse_payload_is_inspectable_and_fallible() {
968 #[derive(Debug)]
969 struct Clean;
970
971 let ready: frontend::TypedSession<Vec<crate::codec::Frame>, frontend::Ready, Clean> =
972 frontend::TypedSession::with_transport(Vec::new());
973 let parse = Parse {
974 statement: Bytes::from_static(b"statement"),
975 query: Bytes::from_static(b"select encrypted_column"),
976 parameter_types: vec![23],
977 };
978 let (building, query): (
979 frontend::TypedSession<Vec<crate::codec::Frame>, frontend::Building, frontend::Dirty>,
980 Bytes,
981 ) = ready
982 .begin_extended()
983 .parse(parse, |frames, message| {
984 let query = message.query.clone();
985 frames.push(message.to_frame()?);
986 Ok::<_, std::io::Error>(query)
987 })
988 .unwrap();
989 assert_eq!(query, Bytes::from_static(b"select encrypted_column"));
990 assert_eq!(building.into_transport()[0].tag, b'P');
991
992 let ready: frontend::TypedSession<Vec<crate::codec::Frame>, frontend::Ready, Clean> =
993 frontend::TypedSession::with_transport(Vec::new());
994 let invalid = Parse {
995 statement: Bytes::from_static(b"bad\0statement"),
996 query: Bytes::from_static(b"select 1"),
997 parameter_types: vec![],
998 };
999 let (building, error) = ready
1000 .begin_extended()
1001 .parse(invalid, |frames, message| {
1002 frames.push(message.to_frame()?);
1003 Ok::<_, std::io::Error>(())
1004 })
1005 .unwrap_err();
1006 assert_eq!(error.kind(), std::io::ErrorKind::InvalidInput);
1007 assert!(building.into_transport().is_empty());
1008 }
1009
1010 #[test]
1011 fn generated_pre_startup_requires_handshake_before_startup() {
1012 struct Clean;
1013 struct Tcp;
1014 struct Tls(Tcp);
1015
1016 let _typed = pre_startup::Session::new()
1017 .ssl_request()
1018 .accept()
1019 .complete()
1020 .startup();
1021
1022 let mut runtime = pre_startup::RuntimeFsm::new();
1023 runtime.step(pre_startup::Event::SslRequest).unwrap();
1024 assert!(runtime.step(pre_startup::Event::Startup).is_err());
1025 runtime.step(pre_startup::Event::Accept).unwrap();
1026 runtime.step(pre_startup::Event::HandshakeComplete).unwrap();
1027 runtime.step(pre_startup::Event::Startup).unwrap();
1028
1029 let pre_startup: pre_startup::TypedSession<Tcp, pre_startup::PreStartup, Clean> =
1030 pre_startup::TypedSession::with_transport(Tcp);
1031 let startup = crate::startup::StartupMessage {
1032 version: crate::startup::ProtocolVersion::V3_0,
1033 parameters: BTreeMap::new(),
1034 };
1035 let auth = pre_startup
1036 .ssl_request()
1037 .accept()
1038 .map_transport(Tls)
1039 .complete()
1040 .startup(startup, |_, startup| {
1041 Ok::<_, std::convert::Infallible>(startup)
1042 });
1043 let (auth, _startup): (
1044 pre_startup::TypedSession<Tls, pre_startup::Auth, Clean>,
1045 crate::startup::StartupMessage,
1046 ) = match auth {
1047 Ok(success) => success,
1048 Err((_session, never)) => match never {},
1049 };
1050 let Tls(_tcp) = auth.into_transport();
1051
1052 assert_eq!(
1053 pre_startup::project_internal(
1054 pre_startup::RuntimeState::PreStartup,
1055 &crate::pre_startup::PreStartupMessage::SslRequest,
1056 ),
1057 Some(pre_startup::Event::SslRequest)
1058 );
1059 assert_eq!(
1060 pre_startup::project_external(
1061 pre_startup::RuntimeState::AwaitingSslReply,
1062 &crate::pre_startup::EncryptionReply::Accepted,
1063 ),
1064 Some(pre_startup::Event::Accept)
1065 );
1066 }
1067
1068 #[test]
1069 fn generated_server_pre_startup_is_the_client_facing_dual() {
1070 let _plaintext = server_pre_startup::Session::new()
1071 .ssl_request()
1072 .reject()
1073 .startup();
1074 let _encrypted = server_pre_startup::Session::new()
1075 .ssl_request()
1076 .accept()
1077 .complete()
1078 .startup();
1079
1080 let mut runtime = server_pre_startup::RuntimeFsm::new();
1081 assert_eq!(runtime.choice(), server_pre_startup::ChoiceKind::External);
1082 runtime.step(server_pre_startup::Event::SslRequest).unwrap();
1083 assert_eq!(runtime.choice(), server_pre_startup::ChoiceKind::Internal);
1084 assert!(runtime.step(server_pre_startup::Event::Startup).is_err());
1085 runtime.step(server_pre_startup::Event::Reject).unwrap();
1086 runtime.step(server_pre_startup::Event::Startup).unwrap();
1087 assert_eq!(runtime.state(), server_pre_startup::RuntimeState::Auth);
1088
1089 assert_eq!(
1090 pre_startup::RuntimeFsm::new().dual_event_choice(pre_startup::Event::SslRequest),
1091 Some(pre_startup::ChoiceKind::External)
1092 );
1093 }
1094
1095 #[test]
1096 fn generated_sasl_continuation_is_recursive() {
1097 let _typed = authentication::Session::new()
1098 .sasl()
1099 .initial()
1100 .continue_response()
1101 .response()
1102 .continue_response()
1103 .response()
1104 .final_response()
1105 .verified()
1106 .ok()
1107 .ready();
1108
1109 let mut runtime = authentication::RuntimeFsm::new();
1110 for event in [
1111 authentication::Event::Sasl,
1112 authentication::Event::Initial,
1113 authentication::Event::Continue,
1114 authentication::Event::Response,
1115 authentication::Event::Continue,
1116 authentication::Event::Response,
1117 authentication::Event::Final,
1118 authentication::Event::Verified,
1119 authentication::Event::Ok,
1120 authentication::Event::Ready,
1121 ] {
1122 runtime.step(event).unwrap();
1123 }
1124 assert_eq!(runtime.state(), authentication::RuntimeState::Ready);
1125 assert_eq!(
1126 authentication::project_external(
1127 authentication::RuntimeState::Sasl,
1128 &BackendMessage::Authentication(Authentication::SaslContinue(Bytes::from_static(
1129 b"challenge"
1130 ),)),
1131 ),
1132 Some(authentication::Event::Continue)
1133 );
1134 assert_eq!(
1135 authentication::project_internal(
1136 authentication::RuntimeState::SaslChallenge,
1137 &FrontendMessage::PasswordResponse(Bytes::from_static(b"response")),
1138 ),
1139 Some(authentication::Event::Response)
1140 );
1141 }
1142
1143 #[test]
1144 fn generated_token_authentication_is_recursive() {
1145 let _typed = authentication::Session::new()
1146 .gss()
1147 .response()
1148 .continue_token()
1149 .response()
1150 .ok()
1151 .ready();
1152
1153 let mut runtime = authentication::RuntimeFsm::new();
1154 for event in [
1155 authentication::Event::Gss,
1156 authentication::Event::Response,
1157 authentication::Event::Continue,
1158 authentication::Event::Response,
1159 authentication::Event::Ok,
1160 authentication::Event::Ready,
1161 ] {
1162 runtime.step(event).unwrap();
1163 }
1164 assert_eq!(runtime.state(), authentication::RuntimeState::Ready);
1165 }
1166
1167 #[test]
1168 fn generated_server_authentication_keeps_mechanisms_independent() {
1169 let _typed = server_authentication::Session::new()
1170 .begin()
1171 .negotiate()
1172 .sasl()
1173 .initial()
1174 .continue_response()
1175 .response()
1176 .final_response()
1177 .ok()
1178 .parameter_status()
1179 .backend_key_data()
1180 .ready();
1181
1182 let mut runtime = server_authentication::RuntimeFsm::new();
1183 for event in [
1184 server_authentication::Event::Begin,
1185 server_authentication::Event::Negotiate,
1186 server_authentication::Event::Gss,
1187 server_authentication::Event::Response,
1188 server_authentication::Event::Continue,
1189 server_authentication::Event::Response,
1190 server_authentication::Event::Verified,
1191 server_authentication::Event::Ok,
1192 server_authentication::Event::Ready,
1193 ] {
1194 runtime.step(event).unwrap();
1195 }
1196 assert_eq!(runtime.state(), server_authentication::RuntimeState::Ready);
1197 }
1198
1199 #[test]
1200 fn runtime_fsm_tracks_the_handwritten_extended_typestate() {
1201 let message = StartupMessage {
1202 version: ProtocolVersion::V3_2,
1203 parameters: BTreeMap::new(),
1204 };
1205 let (startup, _) = Conn::new(()).startup(&message).unwrap();
1206 let AuthOffer::Ok(awaiting_ready) =
1207 startup.authentication().offer(Authentication::Ok).unwrap()
1208 else {
1209 panic!("authentication projected to the wrong branch")
1210 };
1211 let ready = awaiting_ready
1212 .offer_ready(SessionItem::ReadyForQuery {
1213 status: TransactionStatus::Idle,
1214 parameters_changed: false,
1215 })
1216 .unwrap();
1217 let mut runtime = RuntimeFsm::new();
1218
1219 let building = ready.begin_extended();
1220 runtime.step(Event::BeginExtended).unwrap();
1221 let (building, _) = building
1222 .push_parse(&Parse {
1223 statement: Bytes::from_static(b"s"),
1224 query: Bytes::from_static(b"select $1"),
1225 parameter_types: vec![23],
1226 })
1227 .unwrap();
1228 runtime.step(Event::Parse).unwrap();
1229 let (bound, _) = building
1230 .push_bind(&Bind {
1231 portal: Bytes::new(),
1232 statement: Bytes::from_static(b"s"),
1233 parameter_formats: vec![],
1234 parameters: vec![Some(Bytes::from_static(b"42"))],
1235 result_formats: vec![],
1236 })
1237 .unwrap();
1238 runtime.step(Event::Bind).unwrap();
1239 let (bound, _) = bound
1240 .push_execute(&Execute {
1241 portal: Bytes::new(),
1242 max_rows: 0,
1243 })
1244 .unwrap();
1245 runtime.step(Event::Execute).unwrap();
1246 let (awaiting_ready, _) = bound.push_sync();
1247 runtime.step(Event::Sync).unwrap();
1248 let AwaitingReadyTransition::Ready(ReadyState::Clean(ready)) =
1249 awaiting_ready.offer(SessionItem::ReadyForQuery {
1250 status: TransactionStatus::Idle,
1251 parameters_changed: false,
1252 })
1253 else {
1254 panic!("ready evidence projected to the wrong branch")
1255 };
1256 runtime.step(Event::Ready).unwrap();
1257
1258 assert_eq!(runtime.state(), RuntimeState::Ready);
1259 ready.into_transport();
1260 }
1261}