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