1use std::{collections::VecDeque, convert::Infallible, sync::Arc};
14
15use tokio::sync::Notify;
16
17use crate::{
18 codec::{BackendMessage, FrontendMessage},
19 demux::Demux,
20 grammar::backend,
21 middleware::{
22 AsynchronousBackendMessage, ChainError, MessageMiddleware, Middleware,
23 ReconstructableMessage as _, Then,
24 },
25};
26
27#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
29pub(crate) struct OperationId(u64);
30
31#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
33pub struct NoPipeline;
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub struct BoundedPipeline {
38 max_operations: usize,
39}
40
41impl BoundedPipeline {
42 pub fn new(max_operations: usize) -> Result<Self, PipelineConfigError> {
48 if max_operations == 0 {
49 return Err(PipelineConfigError);
50 }
51 Ok(Self { max_operations })
52 }
53
54 #[must_use]
56 pub const fn max_operations(self) -> usize {
57 self.max_operations
58 }
59}
60
61#[derive(Clone, Copy, Debug, Eq, PartialEq)]
63pub struct PipelineConfigError;
64
65impl std::fmt::Display for PipelineConfigError {
66 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 formatter.write_str("pipeline operation limit must be non-zero")
68 }
69}
70
71impl std::error::Error for PipelineConfigError {}
72
73mod private {
74 pub trait Sealed {}
75}
76
77pub trait PipelinePolicy: private::Sealed + Copy {
79 fn operation_limit(self) -> usize;
81}
82
83impl private::Sealed for NoPipeline {}
84impl PipelinePolicy for NoPipeline {
85 fn operation_limit(self) -> usize {
86 1
87 }
88}
89
90impl private::Sealed for BoundedPipeline {}
91impl PipelinePolicy for BoundedPipeline {
92 fn operation_limit(self) -> usize {
93 self.max_operations
94 }
95}
96
97#[derive(Clone, Copy, Debug, Eq, PartialEq)]
99pub(crate) enum FrontendHandling {
100 Forward,
102 Local,
104}
105
106#[derive(Debug, Eq, PartialEq)]
108pub(crate) enum FrontendAction {
109 Forward {
111 id: OperationId,
113 message: FrontendMessage,
115 },
116 Discard {
118 id: OperationId,
120 },
121}
122
123#[derive(Debug, Eq, PartialEq)]
125pub(crate) enum FrontendAdmission {
126 Immediate(FrontendAction),
128 Waiting(FrontendAction),
130}
131
132impl FrontendAdmission {
133 #[must_use]
135 pub(crate) fn into_action(self) -> FrontendAction {
136 match self {
137 Self::Immediate(action) | Self::Waiting(action) => action,
138 }
139 }
140}
141
142#[derive(Debug, Eq, PartialEq)]
144pub enum FrontendProjectionError {
145 Capacity(Box<FrontendMessage>),
147 Illegal {
149 state: PipelineState,
151 message: Box<FrontendMessage>,
153 },
154}
155
156#[derive(Debug, Eq, PartialEq)]
158pub(crate) enum BackendAction {
159 Emit(BackendMessage),
161 Deferred(BackendMessage),
163}
164
165#[derive(Debug, Eq, PartialEq)]
167pub struct BackendProjectionError {
168 pub state: PipelineState,
170 pub message: BackendMessage,
172}
173
174#[derive(Debug, Eq, PartialEq)]
175pub(crate) enum BackendSequenceError {
176 Cardinality { expected: usize, actual: usize },
177 Source(BackendMessage),
178 Replacement(BackendMessage),
179 DifferentSpan,
180}
181
182#[derive(Clone, Copy, Debug, Eq, PartialEq)]
184pub enum PipelineState {
185 Ready,
187 Extended,
189 ExtendedError,
191 CopyIn,
193 CopyOut,
195 CopyBoth,
197 Terminated,
199}
200
201#[derive(Debug)]
203pub(crate) enum PipelineMiddlewareError<MiddlewareError, ProjectionError> {
204 Middleware(MiddlewareError),
206 Projection(ProjectionError),
208}
209
210macro_rules! frontend_pipeline_phases {
211 ($consumer:ident) => {
212 $consumer! {
213 Ready => frontend_ready => backend::ReadyExternalMessage,
214 Building => frontend_building => backend::BuildingExternalMessage,
215 ExtendedError => frontend_extended_error => backend::ExtendedErrorExternalMessage,
216 SimpleCopyIn => frontend_simple_copy_in => backend::SimpleCopyInExternalMessage,
217 ExtendedCopyIn => frontend_extended_copy_in => backend::ExtendedCopyInExternalMessage,
218 SimpleCopyBoth => frontend_simple_copy_both => backend::SimpleCopyBothExternalMessage,
219 ExtendedCopyBoth => frontend_extended_copy_both => backend::ExtendedCopyBothExternalMessage,
220 }
221 };
222}
223
224macro_rules! backend_pipeline_phases {
225 ($consumer:ident) => {
226 $consumer! {
227 Asynchronous => backend_asynchronous => AsynchronousBackendMessage,
228 Simple => backend_simple => backend::SimpleInternalMessage,
229 SimpleError => backend_simple_error => backend::SimpleErrorInternalMessage,
230 ParseResponse => backend_parse_response => backend::ParseResponseInternalMessage,
231 BindResponse => backend_bind_response => backend::BindResponseInternalMessage,
232 DescribeResponse => backend_describe_response => backend::DescribeResponseInternalMessage,
233 ExecuteResponse => backend_execute_response => backend::ExecuteResponseInternalMessage,
234 CloseResponse => backend_close_response => backend::CloseResponseInternalMessage,
235 SyncResponse => backend_sync_response => backend::SyncResponseInternalMessage,
236 FunctionResponse => backend_function_response => backend::FunctionResponseInternalMessage,
237 FunctionReady => backend_function_ready => backend::FunctionReadyInternalMessage,
238 SimpleCopyInDone => backend_simple_copy_in_done => backend::SimpleCopyInDoneInternalMessage,
239 SimpleCopyInFailed => backend_simple_copy_in_failed => backend::SimpleCopyInFailedInternalMessage,
240 SimpleCopyOut => backend_simple_copy_out => backend::SimpleCopyOutInternalMessage,
241 SimpleCopyOutDone => backend_simple_copy_out_done => backend::SimpleCopyOutDoneInternalMessage,
242 SimpleCopyReady => backend_simple_copy_ready => backend::SimpleCopyReadyInternalMessage,
243 ExtendedCopyInDone => backend_extended_copy_in_done => backend::ExtendedCopyInDoneInternalMessage,
244 ExtendedCopyInFailed => backend_extended_copy_in_failed => backend::ExtendedCopyInFailedInternalMessage,
245 ExtendedCopyOut => backend_extended_copy_out => backend::ExtendedCopyOutInternalMessage,
246 ExtendedCopyOutDone => backend_extended_copy_out_done => backend::ExtendedCopyOutDoneInternalMessage,
247 SimpleCopyBoth => backend_simple_copy_both => backend::SimpleCopyBothInternalMessage,
248 SimpleCopyBothClientDone => backend_simple_copy_both_client_done => backend::SimpleCopyBothClientDoneInternalMessage,
249 SimpleCopyBothDone => backend_simple_copy_both_done => backend::SimpleCopyBothDoneInternalMessage,
250 SimpleCopyBothFailed => backend_simple_copy_both_failed => backend::SimpleCopyBothFailedInternalMessage,
251 ExtendedCopyBoth => backend_extended_copy_both => backend::ExtendedCopyBothInternalMessage,
252 ExtendedCopyBothClientDone => backend_extended_copy_both_client_done => backend::ExtendedCopyBothClientDoneInternalMessage,
253 ExtendedCopyBothDone => backend_extended_copy_both_done => backend::ExtendedCopyBothDoneInternalMessage,
254 ExtendedCopyBothFailed => backend_extended_copy_both_failed => backend::ExtendedCopyBothFailedInternalMessage,
255 }
256 };
257}
258
259macro_rules! declare_pipeline_hooks {
260 ($($phase:ident => $method:ident => $message:path),+ $(,)?) => {
261 $(
262 #[doc = concat!("Intercepts backend messages in generated `", stringify!($message), "` phase.")]
263 async fn $method(
264 &mut self,
265 _state: &mut State,
266 message: $message,
267 ) -> Result<$message, Self::Error> {
268 Ok(message)
269 }
270 )+
271 };
272}
273
274#[allow(async_fn_in_trait)]
276pub(crate) trait FrontendPipelineMiddleware<State> {
277 type Error;
279 frontend_pipeline_phases!(declare_pipeline_hooks);
280}
281
282#[allow(async_fn_in_trait)]
284pub(crate) trait BackendPipelineMiddleware<State> {
285 type Error;
287 backend_pipeline_phases!(declare_pipeline_hooks);
288}
289
290impl<State> FrontendPipelineMiddleware<State> for crate::middleware::Identity {
291 type Error = Infallible;
292}
293
294impl<State> BackendPipelineMiddleware<State> for crate::middleware::Identity {
295 type Error = Infallible;
296}
297
298macro_rules! chained_pipeline_hooks {
299 ($($phase:ident => $method:ident => $message:ty),+ $(,)?) => {
300 $(
301 async fn $method(
302 &mut self,
303 state: &mut State,
304 message: $message,
305 ) -> Result<$message, Self::Error> {
306 let (first, second) = self.parts_mut();
307 let message = first
308 .$method(state, message)
309 .await
310 .map_err(ChainError::First)?;
311 second
312 .$method(state, message)
313 .await
314 .map_err(ChainError::Second)
315 }
316 )+
317 };
318}
319
320impl<State, First, Second> FrontendPipelineMiddleware<State> for Then<First, Second>
321where
322 First: FrontendPipelineMiddleware<State>,
323 Second: FrontendPipelineMiddleware<State>,
324{
325 type Error = ChainError<First::Error, Second::Error>;
326
327 frontend_pipeline_phases!(chained_pipeline_hooks);
328}
329
330impl<State, First, Second> BackendPipelineMiddleware<State> for Then<First, Second>
331where
332 First: BackendPipelineMiddleware<State>,
333 Second: BackendPipelineMiddleware<State>,
334{
335 type Error = ChainError<First::Error, Second::Error>;
336 backend_pipeline_phases!(chained_pipeline_hooks);
337}
338
339pub(crate) struct PipelineWireAdapter<Handler> {
341 handler: Handler,
342}
343
344impl<Handler> PipelineWireAdapter<Handler> {
345 pub(crate) const fn new(handler: Handler) -> Self {
347 Self { handler }
348 }
349
350 pub(crate) fn into_inner(self) -> Handler {
352 self.handler
353 }
354}
355
356#[derive(Debug)]
358pub(crate) enum FrontendPipelineWireAdapterError<Error> {
359 Middleware(Error),
361 IllegalFrontend(FrontendMessage),
363}
364
365#[derive(Debug)]
367pub(crate) enum BackendPipelineWireAdapterError<Error> {
368 Middleware(Error),
370 Illegal(BackendMessage),
372}
373
374macro_rules! pipeline_adapter_frontend_hooks {
375 ($($phase:ident => $method:ident => $message:ty),+ $(,)?) => {
376 $(
377 async fn $method(
378 &mut self,
379 state: &mut State,
380 message: $message,
381 ) -> Result<$message, Self::Error> {
382 let message: FrontendMessage = message.into();
383 let message = self
384 .handler
385 .intercept(state, message)
386 .await
387 .map_err(FrontendPipelineWireAdapterError::Middleware)?;
388 <$message>::try_from(message)
389 .map_err(FrontendPipelineWireAdapterError::IllegalFrontend)
390 }
391 )+
392 };
393}
394
395macro_rules! pipeline_adapter_backend_hooks {
396 ($ignored:ident => $async_method:ident => AsynchronousBackendMessage, $($phase:ident => $method:ident => $message:ty),+ $(,)?) => {
397 async fn $async_method(
398 &mut self,
399 state: &mut State,
400 message: AsynchronousBackendMessage,
401 ) -> Result<AsynchronousBackendMessage, Self::Error> {
402 let message = self.handler.intercept(state, message.into_wire()).await
403 .map_err(BackendPipelineWireAdapterError::Middleware)?;
404 AsynchronousBackendMessage::try_from(message)
405 .map_err(BackendPipelineWireAdapterError::Illegal)
406 }
407 $(
408 async fn $method(
409 &mut self,
410 state: &mut State,
411 message: $message,
412 ) -> Result<$message, Self::Error> {
413 let message: BackendMessage = message.into();
414 let message = self
415 .handler
416 .intercept(state, message)
417 .await
418 .map_err(BackendPipelineWireAdapterError::Middleware)?;
419 <$message>::try_from(message).map_err(BackendPipelineWireAdapterError::Illegal)
420 }
421 )+
422 };
423}
424
425impl<State, Handler> FrontendPipelineMiddleware<State> for PipelineWireAdapter<Handler>
426where
427 Handler: MessageMiddleware<FrontendMessage, State>,
428{
429 type Error = FrontendPipelineWireAdapterError<Handler::Error>;
430 frontend_pipeline_phases!(pipeline_adapter_frontend_hooks);
431}
432
433impl<State, Handler> BackendPipelineMiddleware<State> for PipelineWireAdapter<Handler>
434where
435 Handler: MessageMiddleware<BackendMessage, State>,
436{
437 type Error = BackendPipelineWireAdapterError<Handler::Error>;
438 backend_pipeline_phases!(pipeline_adapter_backend_hooks);
439}
440
441#[derive(Clone, Copy, Debug, Eq, PartialEq)]
442enum RequestState {
443 Ready,
444 Extended { bound: bool },
445 ExtendedError,
446 CopyIn,
447 CopyOut,
448 CopyBoth,
449 Terminated,
450}
451
452impl RequestState {
453 const fn public(self) -> PipelineState {
454 match self {
455 Self::Ready => PipelineState::Ready,
456 Self::Extended { .. } => PipelineState::Extended,
457 Self::ExtendedError => PipelineState::ExtendedError,
458 Self::CopyIn => PipelineState::CopyIn,
459 Self::CopyOut => PipelineState::CopyOut,
460 Self::CopyBoth => PipelineState::CopyBoth,
461 Self::Terminated => PipelineState::Terminated,
462 }
463 }
464}
465
466#[derive(Clone, Copy, Debug, Eq, PartialEq)]
467enum Origin {
468 Forwarded,
469 Local,
470}
471
472#[derive(Clone, Copy, Debug, Eq, PartialEq)]
473enum OperationKind {
474 Query,
475 FunctionCall,
476 Parse,
477 Bind,
478 Describe,
479 Execute,
480 Close,
481 Flush,
482 Sync,
483 CopyData,
484 CopyDone,
485 CopyFail,
486 Terminate,
487}
488
489#[derive(Clone, Copy, Debug, Eq, PartialEq)]
490enum PreparedResponse {
491 Asynchronous,
492 Emit {
493 head: Operation,
494 response_state: backend::RuntimeState,
495 },
496 Deferred,
497 Illegal,
498}
499
500#[derive(Clone, Copy, Debug, Eq, PartialEq)]
501enum FrontendPhase {
502 Ready,
503 Building,
504 ExtendedError,
505 SimpleCopyIn,
506 ExtendedCopyIn,
507 SimpleCopyBoth,
508 ExtendedCopyBoth,
509}
510
511#[derive(Clone, Copy, Debug)]
512struct PreparedFrontend {
513 phase: FrontendPhase,
514 request_state: RequestState,
515}
516
517#[derive(Clone, Copy, Debug, Eq, PartialEq)]
518struct Operation {
519 id: OperationId,
520 kind: OperationKind,
521 origin: Origin,
522 discarded: bool,
523 response_state: backend::RuntimeState,
524}
525
526#[derive(Debug)]
528pub(crate) struct Pipeline<P = NoPipeline> {
529 policy: P,
530 operations: VecDeque<Operation>,
531 request_state: RequestState,
532 response_state: Option<PipelineState>,
533 next_id: u64,
534 changed: Arc<Notify>,
535}
536
537impl Default for Pipeline<NoPipeline> {
538 fn default() -> Self {
539 Self::new(NoPipeline)
540 }
541}
542
543impl<P: PipelinePolicy> Pipeline<P> {
544 fn snapshot(&self) -> Self {
545 Self {
546 policy: self.policy,
547 operations: self.operations.clone(),
548 request_state: self.request_state,
549 response_state: self.response_state,
550 next_id: self.next_id,
551 changed: Arc::clone(&self.changed),
552 }
553 }
554
555 pub(crate) fn prepare_backend_replacements(
556 &self,
557 sources: &[BackendMessage],
558 replacements: &[BackendMessage],
559 ) -> Result<Self, BackendSequenceError> {
560 if sources.len() != replacements.len() {
561 return Err(BackendSequenceError::Cardinality {
562 expected: sources.len(),
563 actual: replacements.len(),
564 });
565 }
566 let mut source_projection = self.snapshot();
567 for message in sources.iter().cloned() {
568 match source_projection.accept_backend(message) {
569 Ok(BackendAction::Emit(_)) => {}
570 Ok(BackendAction::Deferred(message))
571 | Err(BackendProjectionError { message, .. }) => {
572 return Err(BackendSequenceError::Source(message));
573 }
574 }
575 }
576 let mut replacement_projection = self.snapshot();
577 for message in replacements.iter().cloned() {
578 if !message.is_reconstructable() {
579 return Err(BackendSequenceError::Replacement(message));
580 }
581 match replacement_projection.accept_backend(message) {
582 Ok(BackendAction::Emit(_)) => {}
583 Ok(BackendAction::Deferred(message))
584 | Err(BackendProjectionError { message, .. }) => {
585 return Err(BackendSequenceError::Replacement(message));
586 }
587 }
588 }
589 if source_projection.operations != replacement_projection.operations
590 || source_projection.request_state != replacement_projection.request_state
591 || source_projection.response_state != replacement_projection.response_state
592 {
593 return Err(BackendSequenceError::DifferentSpan);
594 }
595 Ok(replacement_projection)
596 }
597 #[must_use]
599 pub(crate) fn new(policy: P) -> Self {
600 Self {
601 policy,
602 operations: VecDeque::new(),
603 request_state: RequestState::Ready,
604 response_state: None,
605 next_id: 0,
606 changed: Arc::new(Notify::new()),
607 }
608 }
609
610 #[must_use]
612 pub(crate) fn state(&self) -> PipelineState {
613 self.response_state
614 .unwrap_or_else(|| self.request_state.public())
615 }
616
617 #[must_use]
619 pub(crate) fn len(&self) -> usize {
620 self.operations.len()
621 }
622
623 #[must_use]
625 pub(crate) fn is_empty(&self) -> bool {
626 self.operations.is_empty()
627 }
628
629 pub(crate) fn accept_frontend(
639 &mut self,
640 message: FrontendMessage,
641 handling: FrontendHandling,
642 ) -> Result<FrontendAdmission, FrontendProjectionError> {
643 let prepared = self.prepare_frontend(&message)?;
644 Ok(self.commit_frontend(prepared, message, handling))
645 }
646
647 fn prepare_frontend(
648 &self,
649 message: &FrontendMessage,
650 ) -> Result<PreparedFrontend, FrontendProjectionError> {
651 if self.operations.len() == self.policy.operation_limit()
652 && !matches!(
653 self.request_state,
654 RequestState::CopyIn | RequestState::CopyBoth
655 )
656 {
657 return Err(FrontendProjectionError::Capacity(Box::new(message.clone())));
658 }
659 if project_frontend(self.request_state, message).is_none() {
660 return Err(FrontendProjectionError::Illegal {
661 state: self.state(),
662 message: Box::new(message.clone()),
663 });
664 }
665 Ok(PreparedFrontend {
666 phase: frontend_phase(
667 self.request_state,
668 self.operations.front().map(|operation| operation.kind),
669 ),
670 request_state: self.request_state,
671 })
672 }
673
674 fn commit_frontend(
675 &mut self,
676 prepared: PreparedFrontend,
677 message: FrontendMessage,
678 handling: FrontendHandling,
679 ) -> FrontendAdmission {
680 let (kind, next_state) = classify_frontend(prepared.request_state, &message)
681 .expect("phase-typed frontend replacement has a ledger classification");
682 let waiting = !self.operations.is_empty();
683 let id = OperationId(self.next_id);
684 self.next_id = self.next_id.saturating_add(1);
685 self.request_state = next_state;
686 let origin = match handling {
687 FrontendHandling::Forward => Origin::Forwarded,
688 FrontendHandling::Local => Origin::Local,
689 };
690 if let Some(head) = self.operations.front_mut()
691 && let Some(event) = backend::project_external(head.response_state, &message)
692 && let Some(transition) = backend::transition(head.response_state, event)
693 {
694 head.response_state = transition.target;
695 }
696 self.operations.push_back(Operation {
697 id,
698 kind,
699 origin,
700 discarded: matches!(self.request_state, RequestState::ExtendedError)
701 && kind != OperationKind::Sync,
702 response_state: initial_response_state(kind),
703 });
704 let action = match handling {
705 FrontendHandling::Forward => FrontendAction::Forward { id, message },
706 FrontendHandling::Local => FrontendAction::Discard { id },
707 };
708 let admission = if waiting {
709 FrontendAdmission::Waiting(action)
710 } else {
711 FrontendAdmission::Immediate(action)
712 };
713 self.remove_inert_heads();
714 admission
715 }
716
717 pub(crate) async fn accept_frontend_typed<State, Handler>(
728 &mut self,
729 middleware: &mut Middleware<State, Handler>,
730 message: FrontendMessage,
731 handling: FrontendHandling,
732 ) -> Result<FrontendAdmission, PipelineMiddlewareError<Handler::Error, FrontendProjectionError>>
733 where
734 Handler: FrontendPipelineMiddleware<State>,
735 {
736 let prepared = self
737 .prepare_frontend(&message)
738 .map_err(PipelineMiddlewareError::Projection)?;
739
740 let message = self
741 .intercept_frontend(prepared.phase, middleware, message)
742 .await
743 .map_err(PipelineMiddlewareError::Middleware)?;
744 if !message.is_reconstructable() {
745 return Err(PipelineMiddlewareError::Projection(
746 FrontendProjectionError::Illegal {
747 state: self.state(),
748 message: Box::new(message),
749 },
750 ));
751 }
752 Ok(self.commit_frontend(prepared, message, handling))
753 }
754
755 pub(crate) fn accept_backend(
761 &mut self,
762 message: BackendMessage,
763 ) -> Result<BackendAction, BackendProjectionError> {
764 self.accept_response(None, message)
765 }
766
767 pub(crate) async fn accept_backend_typed<State, Handler>(
778 &mut self,
779 middleware: &mut Middleware<State, Handler>,
780 message: BackendMessage,
781 ) -> Result<BackendAction, PipelineMiddlewareError<Handler::Error, BackendProjectionError>>
782 where
783 Handler: BackendPipelineMiddleware<State>,
784 {
785 self.accept_response_typed(None, middleware, message).await
786 }
787
788 pub(crate) fn try_emit_local(
797 &mut self,
798 id: OperationId,
799 message: BackendMessage,
800 ) -> Result<BackendAction, BackendProjectionError> {
801 self.accept_response(Some(id), message)
802 }
803
804 pub(crate) async fn try_emit_local_typed<State, Handler>(
813 &mut self,
814 middleware: &mut Middleware<State, Handler>,
815 id: OperationId,
816 message: BackendMessage,
817 ) -> Result<BackendAction, PipelineMiddlewareError<Handler::Error, BackendProjectionError>>
818 where
819 Handler: BackendPipelineMiddleware<State>,
820 {
821 self.accept_response_typed(Some(id), middleware, message)
822 .await
823 }
824
825 pub(crate) async fn wait_until_emittable(&self, id: OperationId) {
830 loop {
831 let notified = self.changed.notified();
832 if self
833 .operations
834 .front()
835 .is_some_and(|operation| operation.id == id)
836 {
837 return;
838 }
839 notified.await;
840 }
841 }
842
843 async fn intercept_frontend<State, Handler>(
844 &self,
845 phase: FrontendPhase,
846 middleware: &mut Middleware<State, Handler>,
847 message: FrontendMessage,
848 ) -> Result<FrontendMessage, Handler::Error>
849 where
850 Handler: FrontendPipelineMiddleware<State>,
851 {
852 let (state, handler) = middleware.parts_mut();
853 macro_rules! dispatch {
854 ($message:expr, $type:path, $handler:ident, $state:ident, $method:ident) => {{
855 let Ok(typed) = <$type>::try_from($message) else {
856 unreachable!("frontend message was prevalidated for pipeline phase")
857 };
858 $handler.$method($state, typed).await?.into()
859 }};
860 }
861
862 macro_rules! dispatch_catalogue {
863 ($($catalogue_phase:ident => $method:ident => $message_type:path),+ $(,)?) => {
864 match phase {
865 $(
866 FrontendPhase::$catalogue_phase =>
867 dispatch!(message, $message_type, handler, state, $method),
868 )+
869 }
870 };
871 }
872
873 Ok(frontend_pipeline_phases!(dispatch_catalogue))
874 }
875
876 #[allow(clippy::too_many_lines)]
877 async fn accept_response_typed<State, Handler>(
878 &mut self,
879 local_id: Option<OperationId>,
880 middleware: &mut Middleware<State, Handler>,
881 message: BackendMessage,
882 ) -> Result<BackendAction, PipelineMiddlewareError<Handler::Error, BackendProjectionError>>
883 where
884 Handler: BackendPipelineMiddleware<State>,
885 {
886 let prepared = self.prepare_response(local_id, &message);
887 if matches!(prepared, PreparedResponse::Deferred) {
888 return Ok(BackendAction::Deferred(message));
889 }
890 if matches!(prepared, PreparedResponse::Illegal) {
891 return Err(PipelineMiddlewareError::Projection(
892 BackendProjectionError {
893 state: self.state(),
894 message,
895 },
896 ));
897 }
898
899 let (state, handler) = middleware.parts_mut();
900 let message = match prepared {
901 PreparedResponse::Asynchronous => {
902 let Ok(typed) = AsynchronousBackendMessage::try_from(message) else {
903 unreachable!("asynchronous response was prevalidated")
904 };
905 handler
906 .backend_asynchronous(state, typed)
907 .await
908 .map_err(PipelineMiddlewareError::Middleware)?
909 .into_wire()
910 }
911 PreparedResponse::Emit { response_state, .. } => {
912 macro_rules! dispatch {
913 ($message:ty, $method:ident) => {{
914 let typed = match <$message>::try_from(message) {
915 Ok(typed) => typed,
916 Err(message) => {
917 return Err(PipelineMiddlewareError::Projection(
918 BackendProjectionError {
919 state: self.state(),
920 message,
921 },
922 ));
923 }
924 };
925 handler
926 .$method(state, typed)
927 .await
928 .map_err(PipelineMiddlewareError::Middleware)?
929 .into_wire()
930 }};
931 }
932
933 macro_rules! dispatch_catalogue {
934 ($ignored:ident => $ignored_method:ident => AsynchronousBackendMessage,
935 $($catalogue_phase:ident => $method:ident => $message_type:path),+ $(,)?) => {
936 match response_state {
937 $(
938 backend::RuntimeState::$catalogue_phase =>
939 dispatch!($message_type, $method),
940 )+
941 _ => unreachable!("response phase has no backend-selected transition"),
942 }
943 };
944 }
945
946 backend_pipeline_phases!(dispatch_catalogue)
947 }
948 PreparedResponse::Deferred | PreparedResponse::Illegal => unreachable!(),
949 };
950
951 if !message.is_reconstructable() {
952 return Err(PipelineMiddlewareError::Projection(
953 BackendProjectionError {
954 state: self.state(),
955 message,
956 },
957 ));
958 }
959 self.commit_response(prepared, message)
960 .map_err(PipelineMiddlewareError::Projection)
961 }
962
963 fn prepare_response(
964 &self,
965 local_id: Option<OperationId>,
966 message: &BackendMessage,
967 ) -> PreparedResponse {
968 if is_asynchronous(message) {
969 return PreparedResponse::Asynchronous;
970 }
971 let Some(head) = self.operations.front().copied() else {
972 return PreparedResponse::Illegal;
973 };
974 if let Some(id) = local_id {
975 if head.id != id {
976 return PreparedResponse::Deferred;
977 }
978 if head.origin != Origin::Local {
979 return PreparedResponse::Illegal;
980 }
981 } else if head.origin == Origin::Local {
982 return if self.operations.iter().skip(1).any(|operation| {
983 operation.origin == Origin::Forwarded && response_fits(*operation, message)
984 }) {
985 PreparedResponse::Deferred
986 } else {
987 PreparedResponse::Illegal
988 };
989 }
990 if head.discarded || !response_fits(head, message) {
991 return if self
992 .operations
993 .iter()
994 .skip(1)
995 .any(|operation| response_fits(*operation, message))
996 {
997 PreparedResponse::Deferred
998 } else {
999 PreparedResponse::Illegal
1000 };
1001 }
1002 PreparedResponse::Emit {
1003 head,
1004 response_state: head.response_state,
1005 }
1006 }
1007
1008 fn accept_response(
1009 &mut self,
1010 local_id: Option<OperationId>,
1011 message: BackendMessage,
1012 ) -> Result<BackendAction, BackendProjectionError> {
1013 let prepared = self.prepare_response(local_id, &message);
1014 self.commit_response(prepared, message)
1015 }
1016
1017 fn commit_response(
1018 &mut self,
1019 prepared: PreparedResponse,
1020 message: BackendMessage,
1021 ) -> Result<BackendAction, BackendProjectionError> {
1022 let PreparedResponse::Emit {
1023 head,
1024 response_state,
1025 } = prepared
1026 else {
1027 return match prepared {
1028 PreparedResponse::Asynchronous => Ok(BackendAction::Emit(message)),
1029 PreparedResponse::Deferred => Ok(BackendAction::Deferred(message)),
1030 PreparedResponse::Illegal => Err(BackendProjectionError {
1031 state: self.state(),
1032 message,
1033 }),
1034 PreparedResponse::Emit { .. } => unreachable!(),
1035 };
1036 };
1037 let event = backend::project_internal(response_state, &message)
1038 .expect("response was validated against its generated backend phase");
1039 let next_response_state = backend::transition(response_state, event)
1040 .expect("projected backend event has a generated transition")
1041 .target;
1042 let terminal = response_is_terminal(head.kind, &message);
1043 let error = matches!(message, BackendMessage::ErrorResponse(_));
1044 let copy_state = response_copy_state(next_response_state);
1045 if terminal {
1046 self.operations.pop_front();
1047 if error && is_extended_kind(head.kind) {
1048 self.enter_extended_error();
1049 }
1050 } else if let Some(head) = self.operations.front_mut() {
1051 head.response_state = next_response_state;
1052 }
1053 if !terminal {
1054 self.response_state = copy_state.map(RequestState::public);
1055 }
1056 if let Some(state) = copy_state
1057 && matches!(state, RequestState::CopyIn | RequestState::CopyBoth)
1058 {
1059 self.request_state = state;
1060 }
1061 if terminal {
1062 self.response_state = None;
1063 match head.kind {
1064 OperationKind::Sync | OperationKind::Query => {
1065 self.request_state = RequestState::Ready;
1066 }
1067 OperationKind::Execute if !error => {
1068 self.request_state = RequestState::Extended { bound: true };
1069 }
1070 _ => {}
1071 }
1072 }
1073 self.remove_inert_heads();
1074 self.changed.notify_waiters();
1075 Ok(BackendAction::Emit(message))
1076 }
1077
1078 fn enter_extended_error(&mut self) {
1079 self.request_state = RequestState::ExtendedError;
1080 self.response_state = None;
1081 for operation in &mut self.operations {
1082 if operation.kind == OperationKind::Sync {
1083 break;
1084 }
1085 operation.discarded = true;
1086 }
1087 }
1088
1089 fn remove_inert_heads(&mut self) {
1090 let previous_len = self.operations.len();
1091 while self.operations.front().is_some_and(|operation| {
1092 operation.kind == OperationKind::Flush
1093 || operation.kind == OperationKind::CopyData
1094 || operation.kind == OperationKind::CopyDone
1095 || operation.kind == OperationKind::CopyFail
1096 || operation.kind == OperationKind::Terminate
1097 || operation.discarded
1098 }) {
1099 self.operations.pop_front();
1100 }
1101 if self.operations.len() != previous_len {
1102 self.changed.notify_waiters();
1103 }
1104 }
1105}
1106
1107fn project_frontend(
1108 state: RequestState,
1109 message: &FrontendMessage,
1110) -> Option<(OperationKind, RequestState)> {
1111 use RequestState as S;
1112 let generated_state = match state {
1113 S::Ready => backend::RuntimeState::Ready,
1114 S::Extended { .. } => backend::RuntimeState::Building,
1115 S::ExtendedError => backend::RuntimeState::ExtendedError,
1116 S::CopyIn => backend::RuntimeState::ExtendedCopyIn,
1117 S::CopyOut => backend::RuntimeState::ExtendedCopyOut,
1118 S::CopyBoth => backend::RuntimeState::ExtendedCopyBoth,
1119 S::Terminated => backend::RuntimeState::Terminated,
1120 };
1121 backend::project_external(generated_state, message)?;
1122 classify_frontend(state, message)
1123}
1124
1125fn classify_frontend(
1126 state: RequestState,
1127 message: &FrontendMessage,
1128) -> Option<(OperationKind, RequestState)> {
1129 use FrontendMessage as F;
1130 use OperationKind as O;
1131 use RequestState as S;
1132
1133 match (state, message) {
1134 (S::Ready, F::Query(_)) => Some((O::Query, S::Ready)),
1135 (S::Ready, F::FunctionCall(_)) => Some((O::FunctionCall, S::Ready)),
1136 (S::Ready, F::Parse(_)) => Some((O::Parse, S::Extended { bound: false })),
1137 (S::Ready | S::Extended { .. }, F::Bind(_)) => Some((O::Bind, S::Extended { bound: true })),
1138 (S::Ready, F::Describe(_)) => Some((O::Describe, S::Extended { bound: false })),
1139 (S::Ready | S::Extended { .. }, F::Execute(_)) => {
1140 Some((O::Execute, S::Extended { bound: true }))
1141 }
1142 (S::Ready, F::Close(_)) => Some((O::Close, S::Extended { bound: false })),
1143 (S::Ready, F::Terminate) => Some((O::Terminate, S::Terminated)),
1144 (S::Extended { bound }, F::Parse(_)) => Some((O::Parse, S::Extended { bound })),
1145 (S::Extended { bound }, F::Describe(_)) => Some((O::Describe, S::Extended { bound })),
1146 (S::Extended { bound }, F::Close(_)) => Some((O::Close, S::Extended { bound })),
1147 (S::Extended { bound }, F::Flush) => Some((O::Flush, S::Extended { bound })),
1148 (S::Extended { .. } | S::ExtendedError, F::Sync) => Some((O::Sync, S::Ready)),
1149 (S::ExtendedError, _) => Some((classify_discard(message)?, S::ExtendedError)),
1150 (S::CopyIn, F::CopyData(_)) => Some((O::CopyData, S::CopyIn)),
1151 (S::CopyIn, F::CopyDone) => Some((O::CopyDone, S::Extended { bound: true })),
1152 (S::CopyIn, F::CopyFail(_)) => Some((O::CopyFail, S::ExtendedError)),
1153 (S::CopyBoth, F::CopyData(_)) => Some((O::CopyData, S::CopyBoth)),
1154 (S::CopyBoth, F::CopyDone) => Some((O::CopyDone, S::CopyBoth)),
1155 _ => None,
1156 }
1157}
1158
1159fn frontend_phase(state: RequestState, response_head: Option<OperationKind>) -> FrontendPhase {
1160 match (state, response_head) {
1161 (RequestState::Ready, _) => FrontendPhase::Ready,
1162 (RequestState::Extended { .. }, _) => FrontendPhase::Building,
1163 (RequestState::ExtendedError, _) => FrontendPhase::ExtendedError,
1164 (RequestState::CopyIn, Some(OperationKind::Query)) => FrontendPhase::SimpleCopyIn,
1165 (RequestState::CopyIn, Some(OperationKind::Execute)) => FrontendPhase::ExtendedCopyIn,
1166 (RequestState::CopyBoth, Some(OperationKind::Query)) => FrontendPhase::SimpleCopyBoth,
1167 (RequestState::CopyBoth, Some(OperationKind::Execute)) => FrontendPhase::ExtendedCopyBoth,
1168 (RequestState::CopyIn | RequestState::CopyBoth, _) => {
1169 unreachable!("COPY phase must belong to Query or Execute")
1170 }
1171 (RequestState::CopyOut | RequestState::Terminated, _) => {
1172 unreachable!("non-accepting frontend phase cannot be prepared")
1173 }
1174 }
1175}
1176
1177fn classify_discard(message: &FrontendMessage) -> Option<OperationKind> {
1178 Some(match message {
1179 FrontendMessage::Parse(_) => OperationKind::Parse,
1180 FrontendMessage::Bind(_) => OperationKind::Bind,
1181 FrontendMessage::Describe(_) => OperationKind::Describe,
1182 FrontendMessage::Execute(_) => OperationKind::Execute,
1183 FrontendMessage::Close(_) => OperationKind::Close,
1184 FrontendMessage::Flush => OperationKind::Flush,
1185 FrontendMessage::Query(_) => OperationKind::Query,
1186 FrontendMessage::FunctionCall(_) => OperationKind::FunctionCall,
1187 FrontendMessage::CopyData(_) => OperationKind::CopyData,
1188 FrontendMessage::CopyDone => OperationKind::CopyDone,
1189 FrontendMessage::CopyFail(_) => OperationKind::CopyFail,
1190 FrontendMessage::Terminate => OperationKind::Terminate,
1191 FrontendMessage::PasswordResponse(_) => return None,
1192 FrontendMessage::Sync => unreachable!("Sync is classified before discard"),
1193 })
1194}
1195
1196const fn initial_response_state(kind: OperationKind) -> backend::RuntimeState {
1197 use OperationKind as O;
1198 match kind {
1199 O::Query => backend::RuntimeState::Simple,
1200 O::FunctionCall => backend::RuntimeState::FunctionResponse,
1201 O::Parse => backend::RuntimeState::ParseResponse,
1202 O::Bind => backend::RuntimeState::BindResponse,
1203 O::Describe => backend::RuntimeState::DescribeResponse,
1204 O::Execute => backend::RuntimeState::ExecuteResponse,
1205 O::Close => backend::RuntimeState::CloseResponse,
1206 O::Sync => backend::RuntimeState::SyncResponse,
1207 O::Flush | O::CopyData | O::CopyDone | O::CopyFail | O::Terminate => {
1208 backend::RuntimeState::Terminated
1209 }
1210 }
1211}
1212
1213fn response_fits(operation: Operation, message: &BackendMessage) -> bool {
1214 backend::project_internal(operation.response_state, message).is_some()
1215}
1216
1217fn response_is_terminal(kind: OperationKind, message: &BackendMessage) -> bool {
1218 use BackendMessage as B;
1219 use OperationKind as O;
1220 match kind {
1221 O::Query | O::FunctionCall | O::Sync => matches!(message, B::ReadyForQuery(_)),
1222 O::Parse => matches!(message, B::ParseComplete | B::ErrorResponse(_)),
1223 O::Bind => matches!(message, B::BindComplete | B::ErrorResponse(_)),
1224 O::Describe => matches!(
1225 message,
1226 B::RowDescription(_) | B::NoData | B::ErrorResponse(_)
1227 ),
1228 O::Execute => matches!(
1229 message,
1230 B::CommandComplete(_) | B::PortalSuspended | B::ErrorResponse(_)
1231 ),
1232 O::Close => matches!(message, B::CloseComplete | B::ErrorResponse(_)),
1233 O::CopyDone => matches!(
1234 message,
1235 B::CopyDone | B::CommandComplete(_) | B::ErrorResponse(_)
1236 ),
1237 O::CopyFail => matches!(message, B::ErrorResponse(_)),
1238 O::Flush | O::CopyData | O::Terminate => true,
1239 }
1240}
1241
1242fn is_extended_kind(kind: OperationKind) -> bool {
1243 !matches!(
1244 kind,
1245 OperationKind::Query | OperationKind::FunctionCall | OperationKind::Terminate
1246 )
1247}
1248
1249fn response_copy_state(state: backend::RuntimeState) -> Option<RequestState> {
1250 use backend::RuntimeState as S;
1251 match state {
1252 S::SimpleCopyIn | S::ExtendedCopyIn => Some(RequestState::CopyIn),
1253 S::SimpleCopyOut | S::ExtendedCopyOut => Some(RequestState::CopyOut),
1254 S::SimpleCopyBoth
1255 | S::SimpleCopyBothClientDone
1256 | S::SimpleCopyBothServerDone
1257 | S::ExtendedCopyBoth
1258 | S::ExtendedCopyBothClientDone
1259 | S::ExtendedCopyBothServerDone => Some(RequestState::CopyBoth),
1260 _ => None,
1261 }
1262}
1263
1264fn is_asynchronous(message: &BackendMessage) -> bool {
1265 Demux::is_asynchronous(message)
1266}