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