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