1use std::{
4 cell::Cell, collections::HashMap, error::Error, fmt, future::Future, io, marker::PhantomData,
5 pin::Pin,
6};
7
8use bytes::Bytes;
9
10use crate::{
11 Conn, Dirty,
12 auth::Ready,
13 codec::{Bind, Close, Describe, DescribeTarget, Execute, Frame, Parse, TransactionStatus},
14 demux::SessionItem,
15 session::{
16 AwaitingReady, AwaitingReadyTransition, BoundBuilding, Building, Draining,
17 DrainingTransition, ErrorResponse, ReadyState, SimpleQuery,
18 },
19};
20
21pub fn with_resources<R>(operation: impl for<'id> FnOnce(ResourceScope<'id>) -> R) -> R {
23 operation(ResourceScope::new())
24}
25
26pub fn with_connection_resources<S, P, C, R>(
29 conn: Conn<S, P, C>,
30 operation: impl for<'id> FnOnce(ResourceConnection<'id, S, P, C>) -> R,
31) -> R {
32 operation(ResourceConnection {
33 conn,
34 resources: ResourceScope::new(),
35 })
36}
37
38pub async fn with_connection_resources_async<S, P, C, R>(
44 conn: Conn<S, P, C>,
45 operation: impl for<'id> FnOnce(
46 ResourceConnection<'id, S, P, C>,
47 ) -> Pin<Box<dyn Future<Output = R> + 'id>>,
48) -> R {
49 operation(ResourceConnection {
50 conn,
51 resources: ResourceScope::new(),
52 })
53 .await
54}
55
56#[derive(Debug)]
58pub struct ResourceConnection<'id, S, P, C> {
59 conn: Conn<S, P, C>,
60 resources: ResourceScope<'id>,
61}
62
63impl<'id, S, P, C> ResourceConnection<'id, S, P, C> {
64 pub const fn connection(&self) -> &Conn<S, P, C> {
67 &self.conn
68 }
69
70 pub const fn connection_mut(&mut self) -> &mut Conn<S, P, C> {
73 &mut self.conn
74 }
75
76 #[must_use]
79 pub fn statement_is_live(&self, statement: &PreparedStatement<'id>) -> bool {
80 self.resources.statements.get(&statement.upstream_name) == Some(&statement.generation)
81 }
82
83 #[must_use]
86 pub fn portal_is_live(&self, portal: &Portal<'id>) -> bool {
87 self.resources.portals.get(&portal.upstream_name) == Some(&portal.generation)
88 }
89
90 pub fn into_connection(self) -> Conn<S, P, C> {
92 self.conn
93 }
94}
95
96pub type PrepareResult<'id, S> = Result<
98 (
99 ResourceConnection<'id, S, Building, Dirty>,
100 PreparedStatement<'id>,
101 Frame,
102 ),
103 ResourceProtocolError,
104>;
105
106pub type BindResult<'id, S> = Result<
108 (
109 ResourceConnection<'id, S, BoundBuilding, Dirty>,
110 Portal<'id>,
111 Frame,
112 ),
113 ResourceProtocolError,
114>;
115
116pub type BoundPrepareResult<'id, S> = Result<
118 (
119 ResourceConnection<'id, S, BoundBuilding, Dirty>,
120 PreparedStatement<'id>,
121 Frame,
122 ),
123 ResourceProtocolError,
124>;
125
126pub type RebindResult<'id, S> = Result<
128 (
129 ResourceConnection<'id, S, BoundBuilding, Dirty>,
130 Portal<'id>,
131 Frame,
132 ),
133 ResourceProtocolError,
134>;
135
136#[derive(Debug)]
137pub enum ResourceReadyState<'id, S, C> {
139 Clean(ResourceConnection<'id, S, Ready, C>),
141 Dirty {
143 conn: ResourceConnection<'id, S, Ready, Dirty>,
145 status: TransactionStatus,
147 parameters_changed: bool,
149 },
150}
151
152#[derive(Debug)]
153pub enum ResourceAwaitingTransition<'id, S, C> {
155 Continue(ResourceConnection<'id, S, AwaitingReady, C>, SessionItem),
157 Ready(ResourceReadyState<'id, S, C>),
159 Error(ResourceConnection<'id, S, Draining, C>, ErrorResponse),
161}
162
163#[derive(Debug)]
164pub enum ResourceDrainingTransition<'id, S, C> {
166 Continue(ResourceConnection<'id, S, Draining, C>, SessionItem),
168 Ready(ResourceReadyState<'id, S, C>),
170}
171
172#[derive(Debug)]
174pub struct ResourceScope<'id> {
175 statements: HashMap<Bytes, u64>,
176 client_statements: HashMap<Bytes, (Bytes, u64)>,
177 portals: HashMap<Bytes, u64>,
178 client_portals: HashMap<Bytes, (Bytes, u64)>,
179 next_generation: u64,
180 _brand: PhantomData<Cell<&'id ()>>,
181}
182
183#[derive(Debug)]
185pub struct PreparedStatement<'id> {
186 client_name: Bytes,
187 upstream_name: Bytes,
188 generation: u64,
189 _brand: PhantomData<Cell<&'id ()>>,
190}
191
192#[derive(Debug)]
194pub struct Portal<'id> {
195 client_name: Bytes,
196 upstream_name: Bytes,
197 generation: u64,
198 _brand: PhantomData<Cell<&'id ()>>,
199}
200
201#[derive(Clone, Debug, Eq, PartialEq)]
202pub enum ResourceError {
204 StatementNameCollision,
206 PortalNameCollision,
208 UnknownStatement,
210 UnknownPortal,
212}
213
214impl fmt::Display for ResourceError {
215 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
216 formatter.write_str(match self {
217 Self::StatementNameCollision => "prepared statement name collision",
218 Self::PortalNameCollision => "portal name collision",
219 Self::UnknownStatement => "unknown or stale prepared statement",
220 Self::UnknownPortal => "unknown or stale portal",
221 })
222 }
223}
224
225impl Error for ResourceError {}
226
227#[derive(Debug)]
228pub enum ResourceProtocolError {
230 Resource(ResourceError),
232 Wire(io::Error),
234}
235
236impl fmt::Display for ResourceProtocolError {
237 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
238 match self {
239 Self::Resource(error) => error.fmt(formatter),
240 Self::Wire(error) => error.fmt(formatter),
241 }
242 }
243}
244
245impl Error for ResourceProtocolError {
246 fn source(&self) -> Option<&(dyn Error + 'static)> {
247 match self {
248 Self::Resource(error) => Some(error),
249 Self::Wire(error) => Some(error),
250 }
251 }
252}
253
254impl From<ResourceError> for ResourceProtocolError {
255 fn from(error: ResourceError) -> Self {
256 Self::Resource(error)
257 }
258}
259
260impl From<io::Error> for ResourceProtocolError {
261 fn from(error: io::Error) -> Self {
262 Self::Wire(error)
263 }
264}
265
266impl<'id> ResourceScope<'id> {
267 fn new() -> Self {
268 Self {
269 statements: HashMap::new(),
270 client_statements: HashMap::new(),
271 portals: HashMap::new(),
272 client_portals: HashMap::new(),
273 next_generation: 0,
274 _brand: PhantomData,
275 }
276 }
277
278 pub fn simple_query_boundary(&mut self) {
280 self.statements.remove(b"".as_slice());
281 self.client_statements
282 .retain(|_, (upstream, _)| !upstream.is_empty());
283 }
284
285 pub fn transaction_ended(&mut self) {
287 self.portals.remove(b"".as_slice());
288 self.client_portals
289 .retain(|_, (upstream, _)| !upstream.is_empty());
290 }
291
292 pub fn prepare(
298 &mut self,
299 client_name: Bytes,
300 upstream_name: Bytes,
301 query: Bytes,
302 parameter_types: Vec<u32>,
303 ) -> Result<(PreparedStatement<'id>, Parse), ResourceError> {
304 let generation = self.allocate(&client_name, &upstream_name, true)?;
305 if self
306 .statements
307 .insert(upstream_name.clone(), generation)
308 .is_some()
309 && !upstream_name.is_empty()
310 {
311 return Err(ResourceError::StatementNameCollision);
312 }
313 let statement = PreparedStatement {
314 client_name: client_name.clone(),
315 upstream_name: upstream_name.clone(),
316 generation,
317 _brand: PhantomData,
318 };
319 self.client_statements
320 .insert(client_name, (upstream_name.clone(), generation));
321 let message = Parse {
322 statement: upstream_name,
323 query,
324 parameter_types,
325 };
326 Ok((statement, message))
327 }
328
329 pub fn bind(
335 &mut self,
336 statement: &PreparedStatement<'id>,
337 client_name: Bytes,
338 upstream_name: Bytes,
339 parameter_formats: Vec<i16>,
340 parameters: Vec<Option<Bytes>>,
341 result_formats: Vec<i16>,
342 ) -> Result<(Portal<'id>, Bind), ResourceError> {
343 if self.statements.get(&statement.upstream_name) != Some(&statement.generation) {
344 return Err(ResourceError::UnknownStatement);
345 }
346 let generation = self.allocate(&client_name, &upstream_name, false)?;
347 if self
348 .portals
349 .insert(upstream_name.clone(), generation)
350 .is_some()
351 && !upstream_name.is_empty()
352 {
353 return Err(ResourceError::PortalNameCollision);
354 }
355 let portal = Portal {
356 client_name: client_name.clone(),
357 upstream_name: upstream_name.clone(),
358 generation,
359 _brand: PhantomData,
360 };
361 self.client_portals
362 .insert(client_name, (upstream_name.clone(), generation));
363 let message = Bind {
364 portal: upstream_name,
365 statement: statement.upstream_name.clone(),
366 parameter_formats,
367 parameters,
368 result_formats,
369 };
370 Ok((portal, message))
371 }
372
373 pub fn close_statement(
379 &mut self,
380 statement: PreparedStatement<'id>,
381 ) -> Result<Close, ResourceError> {
382 if self.statements.get(&statement.upstream_name) != Some(&statement.generation) {
383 return Err(ResourceError::UnknownStatement);
384 }
385 self.statements.remove(&statement.upstream_name);
386 self.client_statements.retain(|_, (upstream, generation)| {
387 upstream != &statement.upstream_name || *generation != statement.generation
388 });
389 Ok(Close {
390 target: DescribeTarget::Statement,
391 name: statement.upstream_name,
392 })
393 }
394
395 pub fn close_portal(&mut self, portal: Portal<'id>) -> Result<Close, ResourceError> {
401 if self.portals.get(&portal.upstream_name) != Some(&portal.generation) {
402 return Err(ResourceError::UnknownPortal);
403 }
404 self.portals.remove(&portal.upstream_name);
405 self.client_portals.retain(|_, (upstream, generation)| {
406 upstream != &portal.upstream_name || *generation != portal.generation
407 });
408 Ok(Close {
409 target: DescribeTarget::Portal,
410 name: portal.upstream_name,
411 })
412 }
413
414 fn execute(&self, portal: &Portal<'id>, max_rows: i32) -> Result<Execute, ResourceError> {
415 if self.portals.get(&portal.upstream_name) != Some(&portal.generation) {
416 return Err(ResourceError::UnknownPortal);
417 }
418 Ok(portal.execute(max_rows))
419 }
420
421 fn describe_portal(&self, portal: &Portal<'id>) -> Result<Describe, ResourceError> {
422 if self.portals.get(&portal.upstream_name) != Some(&portal.generation) {
423 return Err(ResourceError::UnknownPortal);
424 }
425 Ok(portal.describe())
426 }
427
428 fn describe_statement(
429 &self,
430 statement: &PreparedStatement<'id>,
431 ) -> Result<Describe, ResourceError> {
432 if self.statements.get(&statement.upstream_name) != Some(&statement.generation) {
433 return Err(ResourceError::UnknownStatement);
434 }
435 Ok(statement.describe())
436 }
437
438 #[must_use]
440 pub fn statement(&self, client_name: &[u8]) -> Option<PreparedStatement<'id>> {
441 let (upstream_name, generation) = self.client_statements.get(client_name)?;
442 Some(PreparedStatement {
443 client_name: Bytes::copy_from_slice(client_name),
444 upstream_name: upstream_name.clone(),
445 generation: *generation,
446 _brand: PhantomData,
447 })
448 }
449
450 #[must_use]
452 pub fn portal(&self, client_name: &[u8]) -> Option<Portal<'id>> {
453 let (upstream_name, generation) = self.client_portals.get(client_name)?;
454 Some(Portal {
455 client_name: Bytes::copy_from_slice(client_name),
456 upstream_name: upstream_name.clone(),
457 generation: *generation,
458 _brand: PhantomData,
459 })
460 }
461}
462
463impl<'id, S, C> ResourceConnection<'id, S, Building, C> {
464 pub fn prepare(
470 self,
471 client_name: Bytes,
472 upstream_name: Bytes,
473 query: Bytes,
474 parameter_types: Vec<u32>,
475 ) -> PrepareResult<'id, S> {
476 let Self {
477 conn,
478 mut resources,
479 } = self;
480 let (statement, message) =
481 resources.prepare(client_name, upstream_name, query, parameter_types)?;
482 let (conn, frame) = conn.push_parse(&message)?;
483 Ok((ResourceConnection { conn, resources }, statement, frame))
484 }
485
486 #[allow(clippy::too_many_arguments)]
492 pub fn bind(
493 self,
494 statement: &PreparedStatement<'id>,
495 client_name: Bytes,
496 upstream_name: Bytes,
497 parameter_formats: Vec<i16>,
498 parameters: Vec<Option<Bytes>>,
499 result_formats: Vec<i16>,
500 ) -> BindResult<'id, S> {
501 let Self {
502 conn,
503 mut resources,
504 } = self;
505 let (portal, message) = resources.bind(
506 statement,
507 client_name,
508 upstream_name,
509 parameter_formats,
510 parameters,
511 result_formats,
512 )?;
513 let (conn, frame) = conn.push_bind(&message)?;
514 Ok((ResourceConnection { conn, resources }, portal, frame))
515 }
516
517 pub fn describe_statement(
523 self,
524 statement: &PreparedStatement<'id>,
525 ) -> Result<(Self, Frame), ResourceProtocolError> {
526 let message = self.resources.describe_statement(statement)?;
527 let Self { conn, resources } = self;
528 let (conn, frame) = conn.push_describe(&message)?;
529 Ok((Self { conn, resources }, frame))
530 }
531
532 pub fn close_statement(
538 self,
539 statement: PreparedStatement<'id>,
540 ) -> Result<(Self, Frame), ResourceProtocolError> {
541 let Self {
542 conn,
543 mut resources,
544 } = self;
545 let message = resources.close_statement(statement)?;
546 let (conn, frame) = conn.push_close(&message)?;
547 Ok((Self { conn, resources }, frame))
548 }
549
550 pub fn close_portal(self, portal: Portal<'id>) -> Result<(Self, Frame), ResourceProtocolError> {
556 let Self {
557 conn,
558 mut resources,
559 } = self;
560 let message = resources.close_portal(portal)?;
561 let (conn, frame) = conn.push_close(&message)?;
562 Ok((Self { conn, resources }, frame))
563 }
564
565 #[must_use]
567 pub fn flush(self) -> (Self, Frame) {
568 let Self { conn, resources } = self;
569 let (conn, frame) = conn.push_flush();
570 (Self { conn, resources }, frame)
571 }
572
573 #[must_use]
575 pub fn sync(self) -> (ResourceConnection<'id, S, AwaitingReady, C>, Frame) {
576 let Self { conn, resources } = self;
577 let (conn, frame) = conn.push_sync();
578 (ResourceConnection { conn, resources }, frame)
579 }
580}
581
582impl<'id, S, C> ResourceConnection<'id, S, Ready, C> {
583 #[must_use]
585 pub fn begin_extended(self) -> ResourceConnection<'id, S, Building, C> {
586 let Self { conn, resources } = self;
587 ResourceConnection {
588 conn: conn.begin_extended(),
589 resources,
590 }
591 }
592
593 pub fn query(
599 self,
600 query: &[u8],
601 ) -> Result<(ResourceConnection<'id, S, SimpleQuery, Dirty>, Frame), ResourceProtocolError>
602 {
603 let Self {
604 conn,
605 mut resources,
606 } = self;
607 resources.simple_query_boundary();
608 let (conn, frame) = conn.push_query(query)?;
609 Ok((ResourceConnection { conn, resources }, frame))
610 }
611}
612
613impl<'id, S, C> ResourceConnection<'id, S, BoundBuilding, C> {
614 pub fn prepare(
620 self,
621 client_name: Bytes,
622 upstream_name: Bytes,
623 query: Bytes,
624 parameter_types: Vec<u32>,
625 ) -> BoundPrepareResult<'id, S> {
626 let Self {
627 conn,
628 mut resources,
629 } = self;
630 let (statement, message) =
631 resources.prepare(client_name, upstream_name, query, parameter_types)?;
632 let (conn, frame) = conn.push_parse(&message)?;
633 Ok((ResourceConnection { conn, resources }, statement, frame))
634 }
635
636 #[allow(clippy::too_many_arguments)]
642 pub fn bind(
643 self,
644 statement: &PreparedStatement<'id>,
645 client_name: Bytes,
646 upstream_name: Bytes,
647 parameter_formats: Vec<i16>,
648 parameters: Vec<Option<Bytes>>,
649 result_formats: Vec<i16>,
650 ) -> RebindResult<'id, S> {
651 let Self {
652 conn,
653 mut resources,
654 } = self;
655 let (portal, message) = resources.bind(
656 statement,
657 client_name,
658 upstream_name,
659 parameter_formats,
660 parameters,
661 result_formats,
662 )?;
663 let (conn, frame) = conn.push_bind(&message)?;
664 Ok((ResourceConnection { conn, resources }, portal, frame))
665 }
666
667 pub fn describe_statement(
673 self,
674 statement: &PreparedStatement<'id>,
675 ) -> Result<(Self, Frame), ResourceProtocolError> {
676 let message = self.resources.describe_statement(statement)?;
677 let Self { conn, resources } = self;
678 let (conn, frame) = conn.push_describe(&message)?;
679 Ok((Self { conn, resources }, frame))
680 }
681
682 pub fn describe_portal(
688 self,
689 portal: &Portal<'id>,
690 ) -> Result<(Self, Frame), ResourceProtocolError> {
691 let message = self.resources.describe_portal(portal)?;
692 let Self { conn, resources } = self;
693 let (conn, frame) = conn.push_describe(&message)?;
694 Ok((Self { conn, resources }, frame))
695 }
696
697 pub fn execute(
703 self,
704 portal: &Portal<'id>,
705 max_rows: i32,
706 ) -> Result<(Self, Frame), ResourceProtocolError> {
707 let message = self.resources.execute(portal, max_rows)?;
708 let Self { conn, resources } = self;
709 let (conn, frame) = conn.push_execute(&message)?;
710 Ok((Self { conn, resources }, frame))
711 }
712
713 pub fn close_statement(
719 self,
720 statement: PreparedStatement<'id>,
721 ) -> Result<(Self, Frame), ResourceProtocolError> {
722 let Self {
723 conn,
724 mut resources,
725 } = self;
726 let message = resources.close_statement(statement)?;
727 let (conn, frame) = conn.push_close(&message)?;
728 Ok((Self { conn, resources }, frame))
729 }
730
731 pub fn close_portal(self, portal: Portal<'id>) -> Result<(Self, Frame), ResourceProtocolError> {
737 let Self {
738 conn,
739 mut resources,
740 } = self;
741 let message = resources.close_portal(portal)?;
742 let (conn, frame) = conn.push_close(&message)?;
743 Ok((Self { conn, resources }, frame))
744 }
745
746 #[must_use]
748 pub fn flush(self) -> (Self, Frame) {
749 let Self { conn, resources } = self;
750 let (conn, frame) = conn.push_flush();
751 (Self { conn, resources }, frame)
752 }
753
754 #[must_use]
756 pub fn sync(self) -> (ResourceConnection<'id, S, AwaitingReady, C>, Frame) {
757 let Self { conn, resources } = self;
758 let (conn, frame) = conn.push_sync();
759 (ResourceConnection { conn, resources }, frame)
760 }
761}
762
763impl<'id, S, C> ResourceConnection<'id, S, AwaitingReady, C> {
764 #[must_use]
766 pub fn offer(self, item: SessionItem) -> ResourceAwaitingTransition<'id, S, C> {
767 let Self { conn, resources } = self;
768 match conn.offer(item) {
769 AwaitingReadyTransition::Continue(conn, item) => {
770 ResourceAwaitingTransition::Continue(Self { conn, resources }, item)
771 }
772 AwaitingReadyTransition::Ready(ready) => {
773 ResourceAwaitingTransition::Ready(resource_ready(resources, ready))
774 }
775 AwaitingReadyTransition::Error(conn, error) => {
776 ResourceAwaitingTransition::Error(ResourceConnection { conn, resources }, error)
777 }
778 }
779 }
780}
781
782impl<'id, S, C> ResourceConnection<'id, S, Draining, C> {
783 #[must_use]
785 pub fn offer(self, item: SessionItem) -> ResourceDrainingTransition<'id, S, C> {
786 let Self { conn, resources } = self;
787 match conn.offer(item) {
788 DrainingTransition::Continue(conn, item) => {
789 ResourceDrainingTransition::Continue(Self { conn, resources }, item)
790 }
791 DrainingTransition::Ready(ready) => {
792 ResourceDrainingTransition::Ready(resource_ready(resources, ready))
793 }
794 }
795 }
796}
797
798fn resource_ready<S, C>(
799 mut resources: ResourceScope<'_>,
800 ready: ReadyState<S, C>,
801) -> ResourceReadyState<'_, S, C> {
802 match ready {
803 ReadyState::Clean(conn) => {
804 resources.transaction_ended();
805 ResourceReadyState::Clean(ResourceConnection { conn, resources })
806 }
807 ReadyState::Dirty {
808 conn,
809 status,
810 parameters_changed,
811 } => {
812 if status == TransactionStatus::Idle {
813 resources.transaction_ended();
814 }
815 ResourceReadyState::Dirty {
816 conn: ResourceConnection { conn, resources },
817 status,
818 parameters_changed,
819 }
820 }
821 }
822}
823
824impl ResourceScope<'_> {
825 fn allocate(
826 &mut self,
827 client_name: &Bytes,
828 upstream_name: &Bytes,
829 statement: bool,
830 ) -> Result<u64, ResourceError> {
831 let (resources, client_resources) = if statement {
832 (&self.statements, &self.client_statements)
833 } else {
834 (&self.portals, &self.client_portals)
835 };
836 if (!upstream_name.is_empty() && resources.contains_key(upstream_name))
837 || (!client_name.is_empty() && client_resources.contains_key(client_name))
838 {
839 return Err(if statement {
840 ResourceError::StatementNameCollision
841 } else {
842 ResourceError::PortalNameCollision
843 });
844 }
845 let generation = self.next_generation;
846 self.next_generation = self.next_generation.saturating_add(1);
847 Ok(generation)
848 }
849}
850
851impl PreparedStatement<'_> {
852 #[must_use]
854 pub fn client_name(&self) -> &[u8] {
855 &self.client_name
856 }
857
858 #[must_use]
860 pub fn upstream_name(&self) -> &[u8] {
861 &self.upstream_name
862 }
863
864 #[must_use]
866 pub fn describe(&self) -> Describe {
867 Describe {
868 target: DescribeTarget::Statement,
869 name: self.upstream_name.clone(),
870 }
871 }
872}
873
874impl Portal<'_> {
875 #[must_use]
877 pub fn client_name(&self) -> &[u8] {
878 &self.client_name
879 }
880
881 #[must_use]
883 pub fn upstream_name(&self) -> &[u8] {
884 &self.upstream_name
885 }
886
887 #[must_use]
889 pub fn describe(&self) -> Describe {
890 Describe {
891 target: DescribeTarget::Portal,
892 name: self.upstream_name.clone(),
893 }
894 }
895
896 #[must_use]
898 pub fn execute(&self, max_rows: i32) -> Execute {
899 Execute {
900 portal: self.upstream_name.clone(),
901 max_rows,
902 }
903 }
904}
905
906#[cfg(test)]
907mod tests {
908 use super::*;
909
910 #[test]
911 fn resource_connection_sends_only_its_own_branded_portals() {
912 let ready: Conn<(), crate::auth::Ready> = Conn::new(()).transition();
913 with_connection_resources(ready.begin_extended(), |connection| {
914 let (connection, statement, parse) = connection
915 .prepare(
916 Bytes::from_static(b"client_statement"),
917 Bytes::from_static(b"proxy_statement"),
918 Bytes::from_static(b"select $1::int4"),
919 vec![23],
920 )
921 .unwrap();
922 assert_eq!(parse.tag, b'P');
923 let (connection, portal, bind) = connection
924 .bind(
925 &statement,
926 Bytes::from_static(b"client_portal"),
927 Bytes::from_static(b"proxy_portal"),
928 vec![1],
929 vec![Some(Bytes::from_static(b"\0\0\0*"))],
930 vec![1],
931 )
932 .unwrap();
933 assert_eq!(bind.tag, b'B');
934 let (connection, describe) = connection.describe_portal(&portal).unwrap();
935 assert_eq!(describe.tag, b'D');
936 let (connection, execute) = connection.execute(&portal, 0).unwrap();
937 assert_eq!(execute.tag, b'E');
938 let (connection, second_statement, parse) = connection
939 .prepare(
940 Bytes::from_static(b"client_statement_2"),
941 Bytes::from_static(b"proxy_statement_2"),
942 Bytes::from_static(b"select 2"),
943 vec![],
944 )
945 .unwrap();
946 assert_eq!(parse.tag, b'P');
947 let (connection, second_portal, bind) = connection
948 .bind(
949 &second_statement,
950 Bytes::from_static(b"client_portal_2"),
951 Bytes::from_static(b"proxy_portal_2"),
952 vec![],
953 vec![],
954 vec![],
955 )
956 .unwrap();
957 assert_eq!(bind.tag, b'B');
958 let (connection, describe) = connection.describe_statement(&second_statement).unwrap();
959 assert_eq!(describe.tag, b'D');
960 let (connection, flush) = connection.flush();
961 assert_eq!(flush.tag, b'H');
962 let (connection, close) = connection.close_portal(second_portal).unwrap();
963 assert_eq!(close.tag, b'C');
964 let (connection, close) = connection.close_portal(portal).unwrap();
965 assert_eq!(close.tag, b'C');
966 let (connection, close) = connection.close_statement(second_statement).unwrap();
967 assert_eq!(close.tag, b'C');
968 let (connection, close) = connection.close_statement(statement).unwrap();
969 assert_eq!(close.tag, b'C');
970 let (awaiting, sync) = connection.sync();
971 assert_eq!(sync.tag, b'S');
972 let ResourceAwaitingTransition::Continue(awaiting, _) = awaiting.offer(
973 SessionItem::Message(crate::codec::BackendMessage::ParseComplete),
974 ) else {
975 panic!("ParseComplete should retain the awaiting phase")
976 };
977 let ResourceAwaitingTransition::Ready(ResourceReadyState::Clean(ready)) = awaiting
978 .offer(SessionItem::ReadyForQuery {
979 status: TransactionStatus::Idle,
980 parameters_changed: false,
981 })
982 else {
983 panic!("idle readiness should complete the extended cycle")
984 };
985 ready.begin_extended().into_connection().into_transport();
986 });
987 }
988
989 #[test]
990 fn namespace_resolves_client_names_to_rewritten_resources() {
991 with_resources(|mut resources| {
992 let (statement, _) = resources
993 .prepare(
994 Bytes::from_static(b"client-statement"),
995 Bytes::from_static(b"upstream-statement-42"),
996 Bytes::from_static(b"select $1"),
997 vec![25],
998 )
999 .unwrap();
1000 let (portal, _) = resources
1001 .bind(
1002 &statement,
1003 Bytes::from_static(b"client-portal"),
1004 Bytes::from_static(b"upstream-portal-42"),
1005 vec![0],
1006 vec![Some(Bytes::from_static(b"value"))],
1007 vec![1],
1008 )
1009 .unwrap();
1010
1011 assert_eq!(
1012 resources
1013 .statement(b"client-statement")
1014 .unwrap()
1015 .upstream_name(),
1016 b"upstream-statement-42"
1017 );
1018 assert_eq!(
1019 resources.portal(b"client-portal").unwrap().upstream_name(),
1020 b"upstream-portal-42"
1021 );
1022
1023 resources.close_portal(portal).unwrap();
1024 resources.close_statement(statement).unwrap();
1025 assert!(resources.portal(b"client-portal").is_none());
1026 assert!(resources.statement(b"client-statement").is_none());
1027 });
1028 }
1029
1030 #[test]
1031 fn idle_readiness_invalidates_only_the_unnamed_portal() {
1032 let ready: Conn<(), crate::auth::Ready> = Conn::new(()).transition();
1033 with_connection_resources(ready.begin_extended(), |connection| {
1034 let (connection, statement, _) = connection
1035 .prepare(
1036 Bytes::new(),
1037 Bytes::new(),
1038 Bytes::from_static(b"select 1"),
1039 vec![],
1040 )
1041 .unwrap();
1042 let (connection, portal, _) = connection
1043 .bind(
1044 &statement,
1045 Bytes::new(),
1046 Bytes::new(),
1047 vec![],
1048 vec![],
1049 vec![],
1050 )
1051 .unwrap();
1052 assert!(connection.statement_is_live(&statement));
1053 assert!(connection.portal_is_live(&portal));
1054 let (awaiting, _) = connection.sync();
1055 let ResourceAwaitingTransition::Ready(ResourceReadyState::Clean(ready)) = awaiting
1056 .offer(SessionItem::ReadyForQuery {
1057 status: TransactionStatus::Idle,
1058 parameters_changed: false,
1059 })
1060 else {
1061 panic!("idle readiness should complete the extended cycle")
1062 };
1063
1064 assert!(ready.statement_is_live(&statement));
1065 assert!(!ready.portal_is_live(&portal));
1066 ready.into_connection().into_transport();
1067 });
1068 }
1069
1070 #[test]
1071 fn simple_query_invalidates_only_the_unnamed_statement() {
1072 let ready: Conn<(), crate::auth::Ready> = Conn::new(()).transition();
1073 with_connection_resources(ready.begin_extended(), |connection| {
1074 let (connection, unnamed, _) = connection
1075 .prepare(
1076 Bytes::new(),
1077 Bytes::new(),
1078 Bytes::from_static(b"select 1"),
1079 vec![],
1080 )
1081 .unwrap();
1082 let (connection, named, _) = connection
1083 .prepare(
1084 Bytes::from_static(b"client_named"),
1085 Bytes::from_static(b"proxy_named"),
1086 Bytes::from_static(b"select 2"),
1087 vec![],
1088 )
1089 .unwrap();
1090 let (awaiting, _) = connection.sync();
1091 let ResourceAwaitingTransition::Ready(ResourceReadyState::Clean(ready)) = awaiting
1092 .offer(SessionItem::ReadyForQuery {
1093 status: TransactionStatus::Idle,
1094 parameters_changed: false,
1095 })
1096 else {
1097 panic!("idle readiness should complete the extended cycle")
1098 };
1099 assert!(ready.statement_is_live(&unnamed));
1100 assert!(ready.statement_is_live(&named));
1101
1102 let (query, frame) = ready.query(b"select 3").unwrap();
1103 assert_eq!(frame.tag, b'Q');
1104 assert!(!query.statement_is_live(&unnamed));
1105 assert!(query.statement_is_live(&named));
1106 query.into_connection().into_transport();
1107 });
1108 }
1109
1110 #[test]
1111 fn branded_resources_rewrite_names_without_losing_bind_details() {
1112 with_resources(|mut resources| {
1113 let (statement, parse) = resources
1114 .prepare(
1115 Bytes::from_static(b"client_statement"),
1116 Bytes::from_static(b"proxy_7_statement"),
1117 Bytes::from_static(b"select $1::int4"),
1118 vec![23],
1119 )
1120 .unwrap();
1121 assert_eq!(statement.client_name(), b"client_statement");
1122 assert_eq!(parse.statement, Bytes::from_static(b"proxy_7_statement"));
1123
1124 let (portal, bind) = resources
1125 .bind(
1126 &statement,
1127 Bytes::from_static(b"client_portal"),
1128 Bytes::from_static(b"proxy_7_portal"),
1129 vec![1],
1130 vec![Some(Bytes::from_static(b"\0\0\0*"))],
1131 vec![1],
1132 )
1133 .unwrap();
1134 assert_eq!(bind.statement, Bytes::from_static(b"proxy_7_statement"));
1135 assert_eq!(bind.portal, Bytes::from_static(b"proxy_7_portal"));
1136 assert_eq!(bind.parameter_formats, [1]);
1137 assert_eq!(bind.result_formats, [1]);
1138 assert_eq!(portal.execute(0).portal, bind.portal);
1139
1140 resources.close_portal(portal).unwrap();
1141 resources.close_statement(statement).unwrap();
1142 });
1143 }
1144
1145 #[test]
1146 fn unnamed_resources_replace_the_previous_unnamed_resource() {
1147 with_resources(|mut resources| {
1148 let (obsolete, _) = resources
1149 .prepare(
1150 Bytes::new(),
1151 Bytes::new(),
1152 Bytes::from_static(b"select 1"),
1153 vec![],
1154 )
1155 .unwrap();
1156 let (replacement, _) = resources
1157 .prepare(
1158 Bytes::new(),
1159 Bytes::new(),
1160 Bytes::from_static(b"select 2"),
1161 vec![],
1162 )
1163 .expect("unnamed Parse replaces the prior unnamed statement");
1164 assert_eq!(
1165 resources
1166 .bind(
1167 &obsolete,
1168 Bytes::new(),
1169 Bytes::new(),
1170 vec![],
1171 vec![],
1172 vec![],
1173 )
1174 .unwrap_err(),
1175 ResourceError::UnknownStatement
1176 );
1177
1178 let (_, _) = resources
1179 .bind(
1180 &replacement,
1181 Bytes::new(),
1182 Bytes::new(),
1183 vec![],
1184 vec![],
1185 vec![],
1186 )
1187 .unwrap();
1188 resources
1189 .bind(
1190 &replacement,
1191 Bytes::new(),
1192 Bytes::new(),
1193 vec![],
1194 vec![],
1195 vec![],
1196 )
1197 .expect("unnamed Bind replaces the prior unnamed portal");
1198 });
1199 }
1200
1201 #[test]
1202 fn protocol_boundaries_invalidate_unnamed_resource_tokens() {
1203 with_resources(|mut resources| {
1204 let (statement, _) = resources
1205 .prepare(
1206 Bytes::new(),
1207 Bytes::new(),
1208 Bytes::from_static(b"select 1"),
1209 vec![],
1210 )
1211 .unwrap();
1212 let (portal, _) = resources
1213 .bind(
1214 &statement,
1215 Bytes::new(),
1216 Bytes::new(),
1217 vec![],
1218 vec![],
1219 vec![],
1220 )
1221 .unwrap();
1222
1223 resources.simple_query_boundary();
1224 assert_eq!(
1225 resources
1226 .bind(
1227 &statement,
1228 Bytes::from_static(b"p"),
1229 Bytes::from_static(b"p"),
1230 vec![],
1231 vec![],
1232 vec![],
1233 )
1234 .unwrap_err(),
1235 ResourceError::UnknownStatement
1236 );
1237
1238 resources.transaction_ended();
1239 assert_eq!(
1240 resources.close_portal(portal).unwrap_err(),
1241 ResourceError::UnknownPortal
1242 );
1243 });
1244 }
1245}