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