Skip to main content

pg_proto/
codec.rs

1//! Direction-parameterised framing and lossless message decoding.
2
3use std::{io, marker::PhantomData};
4
5use bytes::{Buf, BufMut, Bytes, BytesMut};
6use tokio_util::codec::{Decoder, Encoder};
7
8use crate::startup::ProtocolVersion;
9
10/// Messages sent by a `PostgreSQL` frontend.
11#[derive(Debug)]
12pub enum Frontend {}
13
14/// Messages sent by a `PostgreSQL` backend.
15#[derive(Debug)]
16pub enum Backend {}
17
18/// A validated `PostgreSQL` tagged frame, including its tag but not its length.
19#[derive(Clone, Debug, Eq, PartialEq)]
20pub struct Frame {
21    /// Direction-scoped one-byte message tag.
22    pub tag: u8,
23    /// Message bytes after the tag and four-byte length field.
24    pub body: Bytes,
25}
26
27/// Decoder direction. The same byte tag has different meanings in each direction.
28pub trait Direction {
29    /// Typed message family produced for this direction.
30    type Message;
31
32    /// # Errors
33    ///
34    /// Returns an error when the tag is unknown or its body is malformed.
35    fn decode(frame: Frame) -> io::Result<Self::Message>;
36}
37
38/// A `PostgreSQL` codec which cannot confuse frontend and backend tag alphabets.
39#[derive(Debug)]
40pub struct PgCodec<D> {
41    max_frame_len: usize,
42    _direction: PhantomData<fn() -> D>,
43}
44
45const MAX_PROTOCOL_FRAME_LEN: usize = i32::MAX as usize + 1;
46/// Default total tagged-frame limit, including tag and length.
47pub const DEFAULT_MAX_FRAME_LEN: usize = 16 * 1024 * 1024;
48
49impl<D> Default for PgCodec<D> {
50    fn default() -> Self {
51        Self {
52            max_frame_len: DEFAULT_MAX_FRAME_LEN,
53            _direction: PhantomData,
54        }
55    }
56}
57
58impl<D> PgCodec<D> {
59    /// Creates a codec with a total tagged-frame limit, including tag and length.
60    ///
61    /// # Errors
62    ///
63    /// Rejects limits smaller than an empty frame or larger than `PostgreSQL`'s
64    /// signed int32 length field can represent.
65    pub fn with_max_frame_len(max_frame_len: usize) -> io::Result<Self> {
66        if !(5..=MAX_PROTOCOL_FRAME_LEN).contains(&max_frame_len) {
67            return Err(io::Error::new(
68                io::ErrorKind::InvalidInput,
69                "frame limit is outside PostgreSQL's tagged-frame range",
70            ));
71        }
72        Ok(Self {
73            max_frame_len,
74            _direction: PhantomData,
75        })
76    }
77}
78
79impl<D: Direction> Decoder for PgCodec<D> {
80    type Item = D::Message;
81    type Error = io::Error;
82
83    fn decode(&mut self, source: &mut BytesMut) -> io::Result<Option<Self::Item>> {
84        let Some(frame) = decode_frame(source, self.max_frame_len)? else {
85            return Ok(None);
86        };
87        let tag = frame.tag;
88        D::decode(frame).map(Some).map_err(|error| {
89            io::Error::new(error.kind(), format!("message tag 0x{tag:02x}: {error}"))
90        })
91    }
92}
93
94impl<D> Encoder<Frame> for PgCodec<D> {
95    type Error = io::Error;
96
97    fn encode(&mut self, item: Frame, destination: &mut BytesMut) -> io::Result<()> {
98        let frame_len = item
99            .body
100            .len()
101            .checked_add(5)
102            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "frame too large"))?;
103        if frame_len > self.max_frame_len {
104            return Err(io::Error::new(
105                io::ErrorKind::InvalidInput,
106                "frame exceeds configured limit",
107            ));
108        }
109        let length = item
110            .body
111            .len()
112            .checked_add(4)
113            .and_then(|length| u32::try_from(length).ok())
114            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "frame too large"))?;
115        destination.reserve(item.body.len() + 5);
116        destination.put_u8(item.tag);
117        destination.put_u32(length);
118        destination.extend_from_slice(&item.body);
119        Ok(())
120    }
121}
122
123fn decode_frame(source: &mut BytesMut, max_frame_len: usize) -> io::Result<Option<Frame>> {
124    if source.len() < 5 {
125        source.reserve(5 - source.len());
126        return Ok(None);
127    }
128
129    let length = u32::from_be_bytes(source[1..5].try_into().expect("four-byte slice"));
130    if length < 4 {
131        return Err(io::Error::new(
132            io::ErrorKind::InvalidData,
133            "message length is smaller than its length field",
134        ));
135    }
136    let frame_length = usize::try_from(length)
137        .ok()
138        .and_then(|length| length.checked_add(1))
139        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "message length overflow"))?;
140    if frame_length > MAX_PROTOCOL_FRAME_LEN {
141        return Err(io::Error::new(
142            io::ErrorKind::InvalidData,
143            "message length exceeds PostgreSQL's signed int32 range",
144        ));
145    }
146    if frame_length > max_frame_len {
147        return Err(io::Error::new(
148            io::ErrorKind::InvalidData,
149            "message exceeds configured frame limit",
150        ));
151    }
152    if source.len() < frame_length {
153        source.reserve(frame_length - source.len());
154        return Ok(None);
155    }
156
157    let tag = source[0];
158    let mut bytes = source.split_to(frame_length).freeze();
159    bytes.advance(5);
160    Ok(Some(Frame { tag, body: bytes }))
161}
162
163/// Frontend messages whose contents a rewriting proxy must retain structurally.
164#[derive(Clone, Eq, PartialEq)]
165pub enum FrontendMessage {
166    /// Defines a prepared statement and its parameter OIDs.
167    Parse(Parse),
168    /// Binds parameters and result formats to a portal.
169    Bind(Bind),
170    /// Requests metadata for a statement or portal.
171    Describe(Describe),
172    /// Closes a statement or portal.
173    Close(Close),
174    /// Executes a bound portal.
175    Execute(Execute),
176    /// Invokes the deprecated function-call protocol.
177    FunctionCall(FunctionCall),
178    /// Executes one or more SQL statements through the simple-query protocol.
179    Query(Bytes),
180    /// Requests that buffered backend responses be flushed.
181    Flush,
182    /// Ends an extended-query pipeline and restores error recovery.
183    Sync,
184    /// Gracefully closes the frontend session.
185    Terminate,
186    /// Carries COPY or replication data.
187    CopyData(Bytes),
188    /// Signals successful completion of a COPY input stream.
189    CopyDone,
190    /// Aborts a COPY input stream with a diagnostic message.
191    CopyFail(Bytes),
192    /// Context determines whether this is password, GSS, or a SASL response.
193    PasswordResponse(Bytes),
194}
195
196impl std::fmt::Debug for FrontendMessage {
197    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198        match self {
199            Self::Parse(value) => formatter.debug_tuple("Parse").field(value).finish(),
200            Self::Bind(value) => formatter.debug_tuple("Bind").field(value).finish(),
201            Self::Describe(value) => formatter.debug_tuple("Describe").field(value).finish(),
202            Self::Close(value) => formatter.debug_tuple("Close").field(value).finish(),
203            Self::Execute(value) => formatter.debug_tuple("Execute").field(value).finish(),
204            Self::FunctionCall(value) => {
205                formatter.debug_tuple("FunctionCall").field(value).finish()
206            }
207            Self::Query(value) => formatter.debug_tuple("Query").field(value).finish(),
208            Self::Flush => formatter.write_str("Flush"),
209            Self::Sync => formatter.write_str("Sync"),
210            Self::Terminate => formatter.write_str("Terminate"),
211            Self::CopyData(value) => formatter.debug_tuple("CopyData").field(value).finish(),
212            Self::CopyDone => formatter.write_str("CopyDone"),
213            Self::CopyFail(value) => formatter.debug_tuple("CopyFail").field(value).finish(),
214            Self::PasswordResponse(value) => formatter
215                .debug_tuple("PasswordResponse")
216                .field(&format_args!("[REDACTED; {} bytes]", value.len()))
217                .finish(),
218        }
219    }
220}
221
222impl FrontendMessage {
223    /// Reconstructs a frontend frame after inspection or modification.
224    ///
225    /// # Errors
226    ///
227    /// Returns an error when a structured message contains invalid values.
228    pub fn to_frame(&self) -> io::Result<Frame> {
229        match self {
230            Self::Parse(message) => message.to_frame(),
231            Self::Bind(message) => message.to_frame(),
232            Self::Describe(message) => message.to_frame(),
233            Self::Close(message) => message.to_frame(),
234            Self::Execute(message) => message.to_frame(),
235            Self::FunctionCall(message) => message.to_frame(),
236            Self::Query(query) => cstr_message(b'Q', query),
237            Self::Flush => Ok(empty_message(b'H')),
238            Self::Sync => Ok(empty_message(b'S')),
239            Self::Terminate => Ok(empty_message(b'X')),
240            Self::CopyData(data) => Ok(Frame {
241                tag: b'd',
242                body: data.clone(),
243            }),
244            Self::CopyDone => Ok(empty_message(b'c')),
245            Self::CopyFail(message) => cstr_message(b'f', message),
246            Self::PasswordResponse(data) => Ok(Frame {
247                tag: b'p',
248                body: data.clone(),
249            }),
250        }
251    }
252}
253
254/// Structured `Parse` message.
255#[derive(Clone, Debug, Eq, PartialEq)]
256pub struct Parse {
257    /// Client-visible prepared-statement name; empty denotes the unnamed statement.
258    pub statement: Bytes,
259    /// SQL text supplied by the frontend.
260    pub query: Bytes,
261    /// Declared parameter type OIDs; zero entries request inference.
262    pub parameter_types: Vec<u32>,
263}
264
265impl Parse {
266    /// Reconstructs a checked Parse frame after inspection or rewriting.
267    ///
268    /// # Errors
269    ///
270    /// Returns an error for NUL-containing strings or too many parameter types.
271    pub fn to_frame(&self) -> io::Result<Frame> {
272        let mut body = BytesMut::new();
273        put_cstr(&self.statement, &mut body)?;
274        put_cstr(&self.query, &mut body)?;
275        put_count(self.parameter_types.len(), &mut body)?;
276        for oid in &self.parameter_types {
277            body.put_u32(*oid);
278        }
279        Ok(Frame {
280            tag: b'P',
281            body: body.freeze(),
282        })
283    }
284}
285
286/// Structured `Bind` message.
287#[derive(Clone, Debug, Eq, PartialEq)]
288pub struct Bind {
289    /// Portal name; empty denotes the unnamed portal.
290    pub portal: Bytes,
291    /// Prepared-statement name referenced by the portal.
292    pub statement: Bytes,
293    /// Parameter format codes using `PostgreSQL`'s zero/one/per-value cardinality rules.
294    pub parameter_formats: Vec<i16>,
295    /// Parameter values, with `None` representing SQL `NULL`.
296    pub parameters: Vec<Option<Bytes>>,
297    /// Requested result-column format codes.
298    pub result_formats: Vec<i16>,
299}
300
301impl Bind {
302    /// Reconstructs a checked Bind frame, retaining every format code and value.
303    ///
304    /// # Errors
305    ///
306    /// Returns an error for invalid names, excessive counts, or oversized values.
307    pub fn to_frame(&self) -> io::Result<Frame> {
308        let mut body = BytesMut::new();
309        put_cstr(&self.portal, &mut body)?;
310        put_cstr(&self.statement, &mut body)?;
311        put_i16_vec(&self.parameter_formats, &mut body)?;
312        put_count(self.parameters.len(), &mut body)?;
313        for parameter in &self.parameters {
314            match parameter {
315                None => body.put_i32(-1),
316                Some(value) => {
317                    let length = i32::try_from(value.len())
318                        .map_err(|_| invalid_input("Bind parameter is too large"))?;
319                    body.put_i32(length);
320                    body.extend_from_slice(value);
321                }
322            }
323        }
324        put_i16_vec(&self.result_formats, &mut body)?;
325        Ok(Frame {
326            tag: b'B',
327            body: body.freeze(),
328        })
329    }
330}
331
332/// Structured `Describe` message.
333#[derive(Clone, Debug, Eq, PartialEq)]
334pub struct Describe {
335    /// Namespace in which `name` is resolved.
336    pub target: DescribeTarget,
337    /// Statement or portal name to describe.
338    pub name: Bytes,
339}
340
341/// Namespace selected by `Describe` and `Close` messages.
342#[derive(Clone, Copy, Debug, Eq, PartialEq)]
343pub enum DescribeTarget {
344    /// Prepared-statement namespace.
345    Statement,
346    /// Portal namespace.
347    Portal,
348}
349
350/// Structured `Close` message.
351#[derive(Clone, Debug, Eq, PartialEq)]
352pub struct Close {
353    /// Namespace in which `name` is resolved.
354    pub target: DescribeTarget,
355    /// Statement or portal name to close.
356    pub name: Bytes,
357}
358
359/// Structured `Execute` message.
360#[derive(Clone, Debug, Eq, PartialEq)]
361pub struct Execute {
362    /// Portal to execute.
363    pub portal: Bytes,
364    /// Maximum rows to return; zero requests all rows.
365    pub max_rows: i32,
366}
367
368/// Structured legacy `FunctionCall` message.
369#[derive(Clone, Debug, Eq, PartialEq)]
370pub struct FunctionCall {
371    /// OID of the function to invoke.
372    pub function_oid: u32,
373    /// Per-argument format codes.
374    pub argument_formats: Vec<i16>,
375    /// Function arguments, with `None` representing SQL `NULL`.
376    pub arguments: Vec<Option<Bytes>>,
377    /// Requested result format code.
378    pub result_format: i16,
379}
380
381impl Describe {
382    /// Reconstructs a checked Describe frame.
383    ///
384    /// # Errors
385    ///
386    /// Returns an error if the name contains a NUL byte.
387    pub fn to_frame(&self) -> io::Result<Frame> {
388        let mut body = BytesMut::new();
389        body.put_u8(match self.target {
390            DescribeTarget::Statement => b'S',
391            DescribeTarget::Portal => b'P',
392        });
393        put_cstr(&self.name, &mut body)?;
394        Ok(Frame {
395            tag: b'D',
396            body: body.freeze(),
397        })
398    }
399}
400
401impl Close {
402    /// # Errors
403    ///
404    /// Returns an error if the name contains a NUL byte.
405    pub fn to_frame(&self) -> io::Result<Frame> {
406        named_target_frame(b'C', self.target, &self.name)
407    }
408}
409
410impl Execute {
411    /// # Errors
412    ///
413    /// Returns an error if the portal name contains a NUL byte.
414    pub fn to_frame(&self) -> io::Result<Frame> {
415        let mut body = BytesMut::new();
416        put_cstr(&self.portal, &mut body)?;
417        body.put_i32(self.max_rows);
418        Ok(Frame {
419            tag: b'E',
420            body: body.freeze(),
421        })
422    }
423}
424
425impl FunctionCall {
426    /// # Errors
427    ///
428    /// Returns an error for excessive counts or oversized argument values.
429    pub fn to_frame(&self) -> io::Result<Frame> {
430        let mut body = BytesMut::new();
431        body.put_u32(self.function_oid);
432        put_i16_vec(&self.argument_formats, &mut body)?;
433        put_count(self.arguments.len(), &mut body)?;
434        for argument in &self.arguments {
435            put_nullable(argument.as_ref(), &mut body)?;
436        }
437        body.put_i16(self.result_format);
438        Ok(Frame {
439            tag: b'F',
440            body: body.freeze(),
441        })
442    }
443}
444
445impl Direction for Frontend {
446    type Message = FrontendMessage;
447
448    fn decode(frame: Frame) -> io::Result<Self::Message> {
449        match frame.tag {
450            b'P' => decode_parse(frame.body).map(FrontendMessage::Parse),
451            b'B' => decode_bind(frame.body).map(FrontendMessage::Bind),
452            b'D' => decode_describe(frame.body).map(FrontendMessage::Describe),
453            b'C' => decode_close(frame.body).map(FrontendMessage::Close),
454            b'E' => decode_execute(frame.body).map(FrontendMessage::Execute),
455            b'F' => decode_function_call(frame.body).map(FrontendMessage::FunctionCall),
456            b'H' => decode_empty(&frame.body).map(|()| FrontendMessage::Flush),
457            b'Q' => decode_cstr_body(frame.body).map(FrontendMessage::Query),
458            b'S' => decode_empty(&frame.body).map(|()| FrontendMessage::Sync),
459            b'X' => decode_empty(&frame.body).map(|()| FrontendMessage::Terminate),
460            b'c' => decode_empty(&frame.body).map(|()| FrontendMessage::CopyDone),
461            b'd' => Ok(FrontendMessage::CopyData(frame.body)),
462            b'f' => decode_cstr_body(frame.body).map(FrontendMessage::CopyFail),
463            b'p' => Ok(FrontendMessage::PasswordResponse(frame.body)),
464            tag => Err(unknown_tag("frontend", tag)),
465        }
466    }
467}
468
469/// Backend row metadata retained in reconstructable form.
470#[derive(Clone, Debug, Eq, PartialEq)]
471pub struct RowDescription {
472    /// Result columns in wire order.
473    pub fields: Vec<FieldDescription>,
474}
475
476/// Metadata for one result column.
477#[derive(Clone, Debug, Eq, PartialEq)]
478pub struct FieldDescription {
479    /// Column label presented to the frontend.
480    pub name: Bytes,
481    /// Source table OID, or zero when not associated with a table.
482    pub table_oid: u32,
483    /// One-based source column number, or zero when not applicable.
484    pub column: i16,
485    /// `PostgreSQL` data-type OID.
486    pub type_oid: u32,
487    /// Fixed type width, or `-1` for variable-width types.
488    pub type_size: i16,
489    /// Type-specific modifier, or `-1` when absent.
490    pub type_modifier: i32,
491    /// Result format: zero for text and one for binary.
492    pub format: i16,
493}
494
495impl RowDescription {
496    /// Reconstructs checked result metadata after proxy rewriting.
497    ///
498    /// # Errors
499    ///
500    /// Returns an error for excessive fields or NUL-containing field names.
501    pub fn to_frame(&self) -> io::Result<Frame> {
502        let mut body = BytesMut::new();
503        put_count(self.fields.len(), &mut body)?;
504        for field in &self.fields {
505            put_cstr(&field.name, &mut body)?;
506            body.put_u32(field.table_oid);
507            body.put_i16(field.column);
508            body.put_u32(field.type_oid);
509            body.put_i16(field.type_size);
510            body.put_i32(field.type_modifier);
511            body.put_i16(field.format);
512        }
513        Ok(Frame {
514            tag: b'T',
515            body: body.freeze(),
516        })
517    }
518}
519
520/// Backend authentication request or continuation message.
521#[derive(Clone, Debug, Eq, PartialEq)]
522pub enum Authentication {
523    /// Authentication completed successfully.
524    Ok,
525    /// Requests Kerberos V5 authentication.
526    KerberosV5,
527    /// Requests a cleartext password response.
528    CleartextPassword,
529    /// Requests a `PostgreSQL` MD5 password response.
530    Md5Password {
531        /// Four-byte server challenge salt.
532        salt: [u8; 4],
533    },
534    /// Begins a GSSAPI token exchange.
535    Gss,
536    /// Continues a GSSAPI token exchange with a server token.
537    GssContinue(Bytes),
538    /// Begins an SSPI token exchange.
539    Sspi,
540    /// Offers SASL authentication mechanisms.
541    Sasl {
542        /// Mechanism names in server preference order.
543        mechanisms: Vec<Bytes>,
544    },
545    /// Carries a SASL server-first challenge.
546    SaslContinue(Bytes),
547    /// Carries the SASL server-final message.
548    SaslFinal(Bytes),
549}
550
551/// Transaction state reported by `ReadyForQuery`.
552#[derive(Clone, Copy, Debug, Eq, PartialEq)]
553pub enum TransactionStatus {
554    /// No transaction is active.
555    Idle,
556    /// A transaction is active and has not failed.
557    InTransaction,
558    /// A transaction is active and failed; only rollback is legal.
559    FailedTransaction,
560}
561
562/// Backend response negotiating a requested protocol minor version and options.
563#[derive(Clone, Debug, Eq, PartialEq)]
564pub struct NegotiateProtocolVersion {
565    /// Newest protocol version supported by the server.
566    pub newest: ProtocolVersion,
567    /// Startup option names the server does not support.
568    pub unsupported_options: Vec<Bytes>,
569}
570
571/// Ordered diagnostic fields from an error or notice response.
572#[derive(Clone, Debug, Eq, PartialEq)]
573pub struct DiagnosticResponse {
574    /// Diagnostic fields in their original wire order.
575    pub fields: Vec<DiagnosticField>,
576}
577
578/// One tagged `PostgreSQL` diagnostic field.
579#[derive(Clone, Debug, Eq, PartialEq)]
580pub struct DiagnosticField {
581    /// `PostgreSQL`'s one-byte diagnostic field code.
582    pub code: u8,
583    /// Field value without its terminating NUL byte.
584    pub value: Bytes,
585}
586
587/// Format metadata which begins a COPY sub-protocol.
588#[derive(Clone, Debug, Eq, PartialEq)]
589pub struct CopyResponse {
590    /// Overall COPY format: zero for text and one for binary.
591    pub overall_format: u8,
592    /// Per-column format codes.
593    pub column_formats: Vec<i16>,
594}
595
596/// One result row retaining raw text or binary column values.
597#[derive(Clone, Debug, Eq, PartialEq)]
598pub struct DataRow {
599    /// Column values in result order; `None` represents SQL `NULL`.
600    pub columns: Vec<Option<Bytes>>,
601}
602
603/// Messages sent by a `PostgreSQL` backend.
604#[derive(Clone, Debug, Eq, PartialEq)]
605pub enum BackendMessage {
606    /// Describes the columns of a result set.
607    RowDescription(RowDescription),
608    /// Requests, continues, or completes authentication.
609    Authentication(Authentication),
610    /// Confirms `Parse` completion.
611    ParseComplete,
612    /// Confirms `Bind` completion.
613    BindComplete,
614    /// Confirms `Close` completion.
615    CloseComplete,
616    /// Reports a completed SQL command tag.
617    CommandComplete(Bytes),
618    /// Carries COPY or replication data.
619    CopyData(Bytes),
620    /// Signals completion of backend COPY output.
621    CopyDone,
622    /// Enters COPY IN mode.
623    CopyInResponse(CopyResponse),
624    /// Enters COPY OUT mode.
625    CopyOutResponse(CopyResponse),
626    /// Enters bidirectional COPY mode.
627    CopyBothResponse(CopyResponse),
628    /// Carries one result row.
629    DataRow(DataRow),
630    /// Reports an empty simple-query string.
631    EmptyQueryResponse,
632    /// Reports an error with structured diagnostics.
633    ErrorResponse(DiagnosticResponse),
634    /// Reports that a described statement or portal has no row metadata.
635    NoData,
636    /// Reports a runtime parameter value.
637    ParameterStatus {
638        /// Parameter name.
639        name: Bytes,
640        /// Current parameter value.
641        value: Bytes,
642    },
643    /// Reports a non-fatal notice.
644    NoticeResponse(DiagnosticResponse),
645    /// Delivers an asynchronous `LISTEN`/`NOTIFY` notification.
646    NotificationResponse {
647        /// Process ID of the notifying backend.
648        process_id: u32,
649        /// Notification channel.
650        channel: Bytes,
651        /// Notification payload.
652        payload: Bytes,
653    },
654    /// Supplies the backend cancellation key.
655    BackendKeyData {
656        /// Backend process ID.
657        process_id: u32,
658        /// Backend secret cancellation key.
659        secret_key: Bytes,
660    },
661    /// Marks an idle command boundary and reports transaction state.
662    ReadyForQuery(TransactionStatus),
663    /// Reports inferred or declared prepared-statement parameter OIDs.
664    ParameterDescription(Vec<u32>),
665    /// Reports that an execution stopped at its row limit and may resume.
666    PortalSuspended,
667    /// Returns the legacy function-call result bytes.
668    FunctionCallResponse(Bytes),
669    /// Negotiates protocol version and unsupported startup options.
670    NegotiateProtocolVersion(NegotiateProtocolVersion),
671}
672
673impl BackendMessage {
674    /// Reconstructs a backend frame after inspection or modification.
675    ///
676    /// # Errors
677    ///
678    /// Returns an error when a structured message contains invalid values.
679    pub fn to_frame(&self) -> io::Result<Frame> {
680        match self {
681            Self::RowDescription(message) => message.to_frame(),
682            Self::Authentication(message) => authentication_frame(message),
683            Self::ParseComplete => Ok(empty_message(b'1')),
684            Self::BindComplete => Ok(empty_message(b'2')),
685            Self::CloseComplete => Ok(empty_message(b'3')),
686            Self::CommandComplete(tag) => cstr_message(b'C', tag),
687            Self::CopyData(data) => Ok(Frame {
688                tag: b'd',
689                body: data.clone(),
690            }),
691            Self::CopyDone => Ok(empty_message(b'c')),
692            Self::CopyInResponse(response) => copy_response_frame(b'G', response),
693            Self::CopyOutResponse(response) => copy_response_frame(b'H', response),
694            Self::CopyBothResponse(response) => copy_response_frame(b'W', response),
695            Self::DataRow(row) => row.to_frame(),
696            Self::EmptyQueryResponse => Ok(empty_message(b'I')),
697            Self::ErrorResponse(response) => diagnostic_frame(b'E', response),
698            Self::NoData => Ok(empty_message(b'n')),
699            Self::ParameterStatus { name, value } => {
700                let mut body = BytesMut::new();
701                put_cstr(name, &mut body)?;
702                put_cstr(value, &mut body)?;
703                Ok(Frame {
704                    tag: b'S',
705                    body: body.freeze(),
706                })
707            }
708            Self::NoticeResponse(response) => diagnostic_frame(b'N', response),
709            Self::NotificationResponse {
710                process_id,
711                channel,
712                payload,
713            } => {
714                let mut body = BytesMut::new();
715                body.put_u32(*process_id);
716                put_cstr(channel, &mut body)?;
717                put_cstr(payload, &mut body)?;
718                Ok(Frame {
719                    tag: b'A',
720                    body: body.freeze(),
721                })
722            }
723            Self::BackendKeyData {
724                process_id,
725                secret_key,
726            } => {
727                if !(4..=256).contains(&secret_key.len()) {
728                    return Err(invalid_input("cancellation key length is outside 4..=256"));
729                }
730                let mut body = BytesMut::with_capacity(4 + secret_key.len());
731                body.put_u32(*process_id);
732                body.extend_from_slice(secret_key);
733                Ok(Frame {
734                    tag: b'K',
735                    body: body.freeze(),
736                })
737            }
738            Self::ReadyForQuery(status) => Ok(Frame {
739                tag: b'Z',
740                body: Bytes::copy_from_slice(&[status.as_byte()]),
741            }),
742            Self::ParameterDescription(types) => {
743                let mut body = BytesMut::new();
744                put_count(types.len(), &mut body)?;
745                for oid in types {
746                    body.put_u32(*oid);
747                }
748                Ok(Frame {
749                    tag: b't',
750                    body: body.freeze(),
751                })
752            }
753            Self::PortalSuspended => Ok(empty_message(b's')),
754            Self::FunctionCallResponse(data) => Ok(Frame {
755                tag: b'V',
756                body: data.clone(),
757            }),
758            Self::NegotiateProtocolVersion(message) => message.to_frame(),
759        }
760    }
761}
762
763impl TransactionStatus {
764    const fn as_byte(self) -> u8 {
765        match self {
766            Self::Idle => b'I',
767            Self::InTransaction => b'T',
768            Self::FailedTransaction => b'E',
769        }
770    }
771}
772
773impl NegotiateProtocolVersion {
774    /// Reconstructs protocol negotiation, including unsupported option names.
775    ///
776    /// # Errors
777    ///
778    /// Returns an error for too many options or NUL-containing names.
779    pub fn to_frame(&self) -> io::Result<Frame> {
780        let mut body = BytesMut::new();
781        body.put_u32((u32::from(self.newest.major) << 16) | u32::from(self.newest.minor));
782        let count = u32::try_from(self.unsupported_options.len())
783            .map_err(|_| invalid_input("unsupported option count exceeds u32"))?;
784        body.put_u32(count);
785        for option in &self.unsupported_options {
786            put_cstr(option, &mut body)?;
787        }
788        Ok(Frame {
789            tag: b'v',
790            body: body.freeze(),
791        })
792    }
793}
794
795impl DataRow {
796    /// # Errors
797    ///
798    /// Returns an error for too many columns or oversized values.
799    pub fn to_frame(&self) -> io::Result<Frame> {
800        let mut body = BytesMut::new();
801        put_count(self.columns.len(), &mut body)?;
802        for column in &self.columns {
803            put_nullable(column.as_ref(), &mut body)?;
804        }
805        Ok(Frame {
806            tag: b'D',
807            body: body.freeze(),
808        })
809    }
810}
811
812fn diagnostic_frame(tag: u8, response: &DiagnosticResponse) -> io::Result<Frame> {
813    let mut body = BytesMut::new();
814    for field in &response.fields {
815        if field.code == 0 {
816            return Err(invalid_input("diagnostic field code cannot be zero"));
817        }
818        body.put_u8(field.code);
819        put_cstr(&field.value, &mut body)?;
820    }
821    body.put_u8(0);
822    Ok(Frame {
823        tag,
824        body: body.freeze(),
825    })
826}
827
828fn copy_response_frame(tag: u8, response: &CopyResponse) -> io::Result<Frame> {
829    let mut body = BytesMut::new();
830    body.put_u8(response.overall_format);
831    put_i16_vec(&response.column_formats, &mut body)?;
832    Ok(Frame {
833        tag,
834        body: body.freeze(),
835    })
836}
837
838fn authentication_frame(authentication: &Authentication) -> io::Result<Frame> {
839    let mut body = BytesMut::new();
840    match authentication {
841        Authentication::Ok => body.put_u32(0),
842        Authentication::KerberosV5 => body.put_u32(2),
843        Authentication::CleartextPassword => body.put_u32(3),
844        Authentication::Md5Password { salt } => {
845            body.put_u32(5);
846            body.extend_from_slice(salt);
847        }
848        Authentication::Gss => body.put_u32(7),
849        Authentication::GssContinue(data) => {
850            body.put_u32(8);
851            body.extend_from_slice(data);
852        }
853        Authentication::Sspi => body.put_u32(9),
854        Authentication::Sasl { mechanisms } => {
855            body.put_u32(10);
856            for mechanism in mechanisms {
857                put_cstr(mechanism, &mut body)?;
858            }
859            body.put_u8(0);
860        }
861        Authentication::SaslContinue(data) => {
862            body.put_u32(11);
863            body.extend_from_slice(data);
864        }
865        Authentication::SaslFinal(data) => {
866            body.put_u32(12);
867            body.extend_from_slice(data);
868        }
869    }
870    Ok(Frame {
871        tag: b'R',
872        body: body.freeze(),
873    })
874}
875
876impl Direction for Backend {
877    type Message = BackendMessage;
878
879    fn decode(frame: Frame) -> io::Result<Self::Message> {
880        match frame.tag {
881            b'1' => decode_empty(&frame.body).map(|()| BackendMessage::ParseComplete),
882            b'2' => decode_empty(&frame.body).map(|()| BackendMessage::BindComplete),
883            b'3' => decode_empty(&frame.body).map(|()| BackendMessage::CloseComplete),
884            b'C' => decode_cstr_body(frame.body).map(BackendMessage::CommandComplete),
885            b'c' => decode_empty(&frame.body).map(|()| BackendMessage::CopyDone),
886            b'd' => Ok(BackendMessage::CopyData(frame.body)),
887            b'D' => decode_data_row(frame.body).map(BackendMessage::DataRow),
888            b'E' => decode_diagnostic(frame.body).map(BackendMessage::ErrorResponse),
889            b'G' => decode_copy_response(frame.body).map(BackendMessage::CopyInResponse),
890            b'H' => decode_copy_response(frame.body).map(BackendMessage::CopyOutResponse),
891            b'I' => decode_empty(&frame.body).map(|()| BackendMessage::EmptyQueryResponse),
892            b'n' => decode_empty(&frame.body).map(|()| BackendMessage::NoData),
893            b's' => decode_empty(&frame.body).map(|()| BackendMessage::PortalSuspended),
894            b't' => {
895                decode_parameter_description(frame.body).map(BackendMessage::ParameterDescription)
896            }
897            b'T' => decode_row_description(frame.body).map(BackendMessage::RowDescription),
898            b'V' => Ok(BackendMessage::FunctionCallResponse(frame.body)),
899            b'W' => decode_copy_response(frame.body).map(BackendMessage::CopyBothResponse),
900            b'R' => decode_authentication(frame.body).map(BackendMessage::Authentication),
901            b'S' => decode_parameter_status(frame.body),
902            b'N' => decode_diagnostic(frame.body).map(BackendMessage::NoticeResponse),
903            b'A' => decode_notification(frame.body),
904            b'K' => decode_backend_key_data(frame.body),
905            b'Z' => decode_ready(frame.body),
906            b'v' => decode_negotiate_protocol_version(frame.body),
907            tag => Err(unknown_tag("backend", tag)),
908        }
909    }
910}
911
912fn decode_authentication(mut body: Bytes) -> io::Result<Authentication> {
913    let kind = take_u32(&mut body)?;
914    let auth = match kind {
915        0 => Authentication::Ok,
916        2 => Authentication::KerberosV5,
917        3 => Authentication::CleartextPassword,
918        5 => {
919            require(&body, 4)?;
920            let salt = body.split_to(4);
921            Authentication::Md5Password {
922                salt: salt[..].try_into().expect("four-byte slice"),
923            }
924        }
925        7 => Authentication::Gss,
926        8 => Authentication::GssContinue(body.split_to(body.len())),
927        9 => Authentication::Sspi,
928        10 => {
929            let mut mechanisms = Vec::new();
930            while !body.is_empty() && body[0] != 0 {
931                mechanisms.push(take_cstr(&mut body)?);
932            }
933            require(&body, 1)?;
934            body.advance(1);
935            Authentication::Sasl { mechanisms }
936        }
937        11 => Authentication::SaslContinue(body.split_to(body.len())),
938        12 => Authentication::SaslFinal(body.split_to(body.len())),
939        _ => return Err(invalid("unknown authentication request")),
940    };
941    require_empty(&body)?;
942    Ok(auth)
943}
944
945fn decode_parameter_status(mut body: Bytes) -> io::Result<BackendMessage> {
946    let name = take_cstr(&mut body)?;
947    let value = take_cstr(&mut body)?;
948    require_empty(&body)?;
949    Ok(BackendMessage::ParameterStatus { name, value })
950}
951
952fn decode_notification(mut body: Bytes) -> io::Result<BackendMessage> {
953    let process_id = take_u32(&mut body)?;
954    let channel = take_cstr(&mut body)?;
955    let payload = take_cstr(&mut body)?;
956    require_empty(&body)?;
957    Ok(BackendMessage::NotificationResponse {
958        process_id,
959        channel,
960        payload,
961    })
962}
963
964fn decode_backend_key_data(mut body: Bytes) -> io::Result<BackendMessage> {
965    let process_id = take_u32(&mut body)?;
966    if !(4..=256).contains(&body.len()) {
967        return Err(invalid("cancellation key length is outside 4..=256"));
968    }
969    let secret_key = body;
970    Ok(BackendMessage::BackendKeyData {
971        process_id,
972        secret_key,
973    })
974}
975
976fn decode_ready(mut body: Bytes) -> io::Result<BackendMessage> {
977    require(&body, 1)?;
978    let status = match body.get_u8() {
979        b'I' => TransactionStatus::Idle,
980        b'T' => TransactionStatus::InTransaction,
981        b'E' => TransactionStatus::FailedTransaction,
982        _ => return Err(invalid("unknown transaction status")),
983    };
984    require_empty(&body)?;
985    Ok(BackendMessage::ReadyForQuery(status))
986}
987
988fn decode_negotiate_protocol_version(mut body: Bytes) -> io::Result<BackendMessage> {
989    let newest = take_u32(&mut body)?;
990    let major = u16::try_from(newest >> 16).map_err(|_| invalid("protocol major overflow"))?;
991    let minor = u16::try_from(newest & 0xffff).map_err(|_| invalid("protocol minor overflow"))?;
992    let count = take_u32(&mut body)?;
993    let capacity = usize::try_from(count).map_err(|_| invalid("option count overflow"))?;
994    require_collection_bytes(capacity, body.len(), 1, "unsupported option count")?;
995    let mut unsupported_options = Vec::with_capacity(capacity);
996    for _ in 0..count {
997        unsupported_options.push(take_cstr(&mut body)?);
998    }
999    require_empty(&body)?;
1000    Ok(BackendMessage::NegotiateProtocolVersion(
1001        NegotiateProtocolVersion {
1002            newest: ProtocolVersion { major, minor },
1003            unsupported_options,
1004        },
1005    ))
1006}
1007
1008fn decode_parse(mut body: Bytes) -> io::Result<Parse> {
1009    let statement = take_cstr(&mut body)?;
1010    let query = take_cstr(&mut body)?;
1011    let count = take_u16(&mut body)?;
1012    require_collection_bytes(usize::from(count), body.len(), 4, "parameter type count")?;
1013    let mut parameter_types = Vec::with_capacity(usize::from(count));
1014    for _ in 0..count {
1015        parameter_types.push(take_u32(&mut body)?);
1016    }
1017    require_empty(&body)?;
1018    Ok(Parse {
1019        statement,
1020        query,
1021        parameter_types,
1022    })
1023}
1024
1025fn decode_bind(mut body: Bytes) -> io::Result<Bind> {
1026    let portal = take_cstr(&mut body)?;
1027    let statement = take_cstr(&mut body)?;
1028    let parameter_formats = take_i16_vec(&mut body)?;
1029    let parameter_count = take_u16(&mut body)?;
1030    require_collection_bytes(
1031        usize::from(parameter_count),
1032        body.len(),
1033        4,
1034        "parameter count",
1035    )?;
1036    let mut parameters = Vec::with_capacity(usize::from(parameter_count));
1037    for _ in 0..parameter_count {
1038        let length = take_i32(&mut body)?;
1039        if length == -1 {
1040            parameters.push(None);
1041        } else {
1042            let length =
1043                usize::try_from(length).map_err(|_| invalid("negative parameter length"))?;
1044            require(&body, length)?;
1045            parameters.push(Some(body.split_to(length)));
1046        }
1047    }
1048    let result_formats = take_i16_vec(&mut body)?;
1049    require_empty(&body)?;
1050    Ok(Bind {
1051        portal,
1052        statement,
1053        parameter_formats,
1054        parameters,
1055        result_formats,
1056    })
1057}
1058
1059fn decode_describe(mut body: Bytes) -> io::Result<Describe> {
1060    let target = take_target(&mut body)?;
1061    let name = take_cstr(&mut body)?;
1062    require_empty(&body)?;
1063    Ok(Describe { target, name })
1064}
1065
1066fn decode_data_row(mut body: Bytes) -> io::Result<DataRow> {
1067    let count = take_u16(&mut body)?;
1068    require_collection_bytes(usize::from(count), body.len(), 4, "column count")?;
1069    let mut columns = Vec::with_capacity(usize::from(count));
1070    for _ in 0..count {
1071        columns.push(take_nullable(&mut body)?);
1072    }
1073    require_empty(&body)?;
1074    Ok(DataRow { columns })
1075}
1076
1077fn decode_diagnostic(mut body: Bytes) -> io::Result<DiagnosticResponse> {
1078    let mut fields = Vec::new();
1079    loop {
1080        require(&body, 1)?;
1081        let code = body.get_u8();
1082        if code == 0 {
1083            break;
1084        }
1085        fields.push(DiagnosticField {
1086            code,
1087            value: take_cstr(&mut body)?,
1088        });
1089    }
1090    require_empty(&body)?;
1091    Ok(DiagnosticResponse { fields })
1092}
1093
1094fn decode_copy_response(mut body: Bytes) -> io::Result<CopyResponse> {
1095    require(&body, 1)?;
1096    let overall_format = body.get_u8();
1097    let column_formats = take_i16_vec(&mut body)?;
1098    require_empty(&body)?;
1099    Ok(CopyResponse {
1100        overall_format,
1101        column_formats,
1102    })
1103}
1104
1105fn decode_parameter_description(mut body: Bytes) -> io::Result<Vec<u32>> {
1106    let count = take_u16(&mut body)?;
1107    require_collection_bytes(usize::from(count), body.len(), 4, "parameter type count")?;
1108    let mut types = Vec::with_capacity(usize::from(count));
1109    for _ in 0..count {
1110        types.push(take_u32(&mut body)?);
1111    }
1112    require_empty(&body)?;
1113    Ok(types)
1114}
1115
1116fn decode_close(mut body: Bytes) -> io::Result<Close> {
1117    let target = take_target(&mut body)?;
1118    let name = take_cstr(&mut body)?;
1119    require_empty(&body)?;
1120    Ok(Close { target, name })
1121}
1122
1123fn decode_execute(mut body: Bytes) -> io::Result<Execute> {
1124    let portal = take_cstr(&mut body)?;
1125    let max_rows = take_i32(&mut body)?;
1126    require_empty(&body)?;
1127    Ok(Execute { portal, max_rows })
1128}
1129
1130fn decode_function_call(mut body: Bytes) -> io::Result<FunctionCall> {
1131    let function_oid = take_u32(&mut body)?;
1132    let argument_formats = take_i16_vec(&mut body)?;
1133    let count = take_u16(&mut body)?;
1134    require_collection_bytes(usize::from(count), body.len(), 4, "argument count")?;
1135    let mut arguments = Vec::with_capacity(usize::from(count));
1136    for _ in 0..count {
1137        arguments.push(take_nullable(&mut body)?);
1138    }
1139    let result_format = take_i16(&mut body)?;
1140    require_empty(&body)?;
1141    Ok(FunctionCall {
1142        function_oid,
1143        argument_formats,
1144        arguments,
1145        result_format,
1146    })
1147}
1148
1149fn decode_cstr_body(mut body: Bytes) -> io::Result<Bytes> {
1150    let value = take_cstr(&mut body)?;
1151    require_empty(&body)?;
1152    Ok(value)
1153}
1154
1155fn decode_empty(body: &Bytes) -> io::Result<()> {
1156    require_empty(body)
1157}
1158
1159fn take_target(body: &mut Bytes) -> io::Result<DescribeTarget> {
1160    require(body, 1)?;
1161    match body.get_u8() {
1162        b'S' => Ok(DescribeTarget::Statement),
1163        b'P' => Ok(DescribeTarget::Portal),
1164        _ => Err(invalid("invalid statement or portal target")),
1165    }
1166}
1167
1168fn decode_row_description(mut body: Bytes) -> io::Result<RowDescription> {
1169    let count = take_u16(&mut body)?;
1170    require_collection_bytes(usize::from(count), body.len(), 19, "field count")?;
1171    let mut fields = Vec::with_capacity(usize::from(count));
1172    for _ in 0..count {
1173        fields.push(FieldDescription {
1174            name: take_cstr(&mut body)?,
1175            table_oid: take_u32(&mut body)?,
1176            column: take_i16(&mut body)?,
1177            type_oid: take_u32(&mut body)?,
1178            type_size: take_i16(&mut body)?,
1179            type_modifier: take_i32(&mut body)?,
1180            format: take_i16(&mut body)?,
1181        });
1182    }
1183    require_empty(&body)?;
1184    Ok(RowDescription { fields })
1185}
1186
1187fn take_i16_vec(body: &mut Bytes) -> io::Result<Vec<i16>> {
1188    let count = take_u16(body)?;
1189    require_collection_bytes(usize::from(count), body.len(), 2, "format count")?;
1190    (0..count).map(|_| take_i16(body)).collect()
1191}
1192
1193fn require_collection_bytes(
1194    count: usize,
1195    remaining: usize,
1196    minimum_item_len: usize,
1197    description: &str,
1198) -> io::Result<()> {
1199    let minimum_len = count.checked_mul(minimum_item_len).ok_or_else(|| {
1200        io::Error::new(
1201            io::ErrorKind::InvalidData,
1202            format!("{description} overflows message size"),
1203        )
1204    })?;
1205    if minimum_len > remaining {
1206        return Err(io::Error::new(
1207            io::ErrorKind::InvalidData,
1208            format!("{description} exceeds remaining message body"),
1209        ));
1210    }
1211    Ok(())
1212}
1213
1214fn take_nullable(body: &mut Bytes) -> io::Result<Option<Bytes>> {
1215    let length = take_i32(body)?;
1216    if length == -1 {
1217        return Ok(None);
1218    }
1219    let length = usize::try_from(length).map_err(|_| invalid("negative value length"))?;
1220    require(body, length)?;
1221    Ok(Some(body.split_to(length)))
1222}
1223
1224fn put_i16_vec(values: &[i16], body: &mut BytesMut) -> io::Result<()> {
1225    put_count(values.len(), body)?;
1226    for value in values {
1227        body.put_i16(*value);
1228    }
1229    Ok(())
1230}
1231
1232fn put_nullable(value: Option<&Bytes>, body: &mut BytesMut) -> io::Result<()> {
1233    match value {
1234        None => body.put_i32(-1),
1235        Some(value) => {
1236            let length =
1237                i32::try_from(value.len()).map_err(|_| invalid_input("value is too large"))?;
1238            body.put_i32(length);
1239            body.extend_from_slice(value);
1240        }
1241    }
1242    Ok(())
1243}
1244
1245fn put_count(count: usize, body: &mut BytesMut) -> io::Result<()> {
1246    let count =
1247        u16::try_from(count).map_err(|_| invalid_input("message item count exceeds u16"))?;
1248    body.put_u16(count);
1249    Ok(())
1250}
1251
1252fn put_cstr(value: &[u8], body: &mut BytesMut) -> io::Result<()> {
1253    if value.contains(&0) {
1254        return Err(invalid_input("message string contains a NUL byte"));
1255    }
1256    body.extend_from_slice(value);
1257    body.put_u8(0);
1258    Ok(())
1259}
1260
1261fn named_target_frame(tag: u8, target: DescribeTarget, name: &[u8]) -> io::Result<Frame> {
1262    let mut body = BytesMut::new();
1263    body.put_u8(match target {
1264        DescribeTarget::Statement => b'S',
1265        DescribeTarget::Portal => b'P',
1266    });
1267    put_cstr(name, &mut body)?;
1268    Ok(Frame {
1269        tag,
1270        body: body.freeze(),
1271    })
1272}
1273
1274fn cstr_message(tag: u8, value: &[u8]) -> io::Result<Frame> {
1275    let mut body = BytesMut::new();
1276    put_cstr(value, &mut body)?;
1277    Ok(Frame {
1278        tag,
1279        body: body.freeze(),
1280    })
1281}
1282
1283fn empty_message(tag: u8) -> Frame {
1284    Frame {
1285        tag,
1286        body: Bytes::new(),
1287    }
1288}
1289
1290fn take_cstr(body: &mut Bytes) -> io::Result<Bytes> {
1291    let end = body
1292        .iter()
1293        .position(|byte| *byte == 0)
1294        .ok_or_else(|| invalid("unterminated string"))?;
1295    let value = body.split_to(end);
1296    body.advance(1);
1297    Ok(value)
1298}
1299
1300fn take_u16(body: &mut Bytes) -> io::Result<u16> {
1301    require(body, 2)?;
1302    Ok(body.get_u16())
1303}
1304
1305fn take_i16(body: &mut Bytes) -> io::Result<i16> {
1306    require(body, 2)?;
1307    Ok(body.get_i16())
1308}
1309
1310fn take_u32(body: &mut Bytes) -> io::Result<u32> {
1311    require(body, 4)?;
1312    Ok(body.get_u32())
1313}
1314
1315fn take_i32(body: &mut Bytes) -> io::Result<i32> {
1316    require(body, 4)?;
1317    Ok(body.get_i32())
1318}
1319
1320fn require(body: &Bytes, length: usize) -> io::Result<()> {
1321    if body.len() < length {
1322        Err(invalid("truncated message body"))
1323    } else {
1324        Ok(())
1325    }
1326}
1327
1328fn require_empty(body: &Bytes) -> io::Result<()> {
1329    if body.is_empty() {
1330        Ok(())
1331    } else {
1332        Err(invalid("trailing message bytes"))
1333    }
1334}
1335
1336fn invalid(message: &'static str) -> io::Error {
1337    io::Error::new(io::ErrorKind::InvalidData, message)
1338}
1339
1340fn invalid_input(message: &'static str) -> io::Error {
1341    io::Error::new(io::ErrorKind::InvalidInput, message)
1342}
1343
1344fn unknown_tag(direction: &str, tag: u8) -> io::Error {
1345    io::Error::new(
1346        io::ErrorKind::InvalidData,
1347        format!("unknown {direction} message tag 0x{tag:02x}"),
1348    )
1349}
1350
1351#[cfg(test)]
1352mod tests {
1353    use super::*;
1354
1355    #[test]
1356    fn direction_disambiguates_s_tag() {
1357        let frontend_frame = Frame {
1358            tag: b'S',
1359            body: Bytes::new(),
1360        };
1361        assert!(matches!(
1362            Frontend::decode(frontend_frame),
1363            Ok(FrontendMessage::Sync)
1364        ));
1365        assert_eq!(
1366            Backend::decode(Frame {
1367                tag: b'S',
1368                body: Bytes::from_static(b"client_encoding\0UTF8\0"),
1369            })
1370            .expect("valid ParameterStatus"),
1371            BackendMessage::ParameterStatus {
1372                name: Bytes::from_static(b"client_encoding"),
1373                value: Bytes::from_static(b"UTF8"),
1374            }
1375        );
1376    }
1377
1378    #[test]
1379    fn parse_is_losslessly_structured() {
1380        let mut bytes = BytesMut::from(&b"P\0\0\0\x19stmt\0select $1\0\0\x01\0\0\0\x17"[..]);
1381        let original = bytes.clone();
1382        let message = PgCodec::<Frontend>::default()
1383            .decode(&mut bytes)
1384            .expect("valid frame")
1385            .expect("complete frame");
1386        let expected = FrontendMessage::Parse(Parse {
1387            statement: Bytes::from_static(b"stmt"),
1388            query: Bytes::from_static(b"select $1"),
1389            parameter_types: vec![23],
1390        });
1391        assert_eq!(message, expected);
1392
1393        let FrontendMessage::Parse(parsed) = message else {
1394            unreachable!()
1395        };
1396        let frame = parsed.to_frame().expect("reconstructable Parse");
1397        let mut encoded = BytesMut::new();
1398        PgCodec::<Frontend>::default()
1399            .encode(frame, &mut encoded)
1400            .expect("encodable frame");
1401        assert_eq!(encoded, original);
1402    }
1403
1404    #[test]
1405    fn incomplete_frame_does_not_consume_input() {
1406        let mut bytes = BytesMut::from(&b"S\0\0\0\x04"[..4]);
1407        let original = bytes.clone();
1408        assert!(
1409            PgCodec::<Frontend>::default()
1410                .decode(&mut bytes)
1411                .expect("incomplete input is not an error")
1412                .is_none()
1413        );
1414        assert_eq!(bytes, original);
1415    }
1416
1417    #[test]
1418    fn frame_limits_reject_oversized_input_before_allocation() {
1419        let mut codec = PgCodec::<Frontend>::with_max_frame_len(9).unwrap();
1420        let mut oversized = BytesMut::from(&b"Q\0\0\0\x09"[..]);
1421        let error = codec.decode(&mut oversized).unwrap_err();
1422        assert_eq!(error.kind(), io::ErrorKind::InvalidData);
1423        assert_eq!(oversized.len(), 5);
1424
1425        let mut signed_overflow = BytesMut::from(&[b'Q', 0x80, 0, 0, 0][..]);
1426        let error = PgCodec::<Frontend>::default()
1427            .decode(&mut signed_overflow)
1428            .unwrap_err();
1429        assert_eq!(error.kind(), io::ErrorKind::InvalidData);
1430
1431        let error = codec
1432            .encode(
1433                Frame {
1434                    tag: b'Q',
1435                    body: Bytes::from_static(b"12345"),
1436                },
1437                &mut BytesMut::new(),
1438            )
1439            .unwrap_err();
1440        assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
1441    }
1442
1443    #[test]
1444    fn counted_backend_fields_are_bounded_before_allocation() {
1445        // Regression input discovered by the backend codec fuzz target. The
1446        // 47-byte frame declares 1,291,845,632 unsupported options and formerly
1447        // attempted to reserve roughly 34 GB before parsing the first option.
1448        let mut input = BytesMut::from(
1449            &[
1450                b'v', 0, 0, 0, 43, 0, 0, 64, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1451                32, 0, 0, 173, 173, 173, 173, 173, 173, 173, 173, 0, 87, 0, 0, 0, 0, 0, 0, 0, 0,
1452                152,
1453            ][..],
1454        );
1455
1456        let error = PgCodec::<Backend>::default()
1457            .decode(&mut input)
1458            .expect_err("impossible option count must be rejected");
1459
1460        assert_eq!(error.kind(), io::ErrorKind::InvalidData);
1461        assert!(
1462            error
1463                .to_string()
1464                .contains("unsupported option count exceeds remaining message body")
1465        );
1466    }
1467
1468    #[test]
1469    fn default_frame_limit_is_network_safe_and_configurable() {
1470        let mut input = BytesMut::from(&[b'Q', 1, 0, 0, 4][..]);
1471        let error = PgCodec::<Frontend>::default()
1472            .decode(&mut input)
1473            .unwrap_err();
1474        assert_eq!(error.kind(), io::ErrorKind::InvalidData);
1475        assert!(error.to_string().contains("configured frame limit"));
1476
1477        assert!(PgCodec::<Frontend>::with_max_frame_len(MAX_PROTOCOL_FRAME_LEN).is_ok());
1478    }
1479
1480    #[test]
1481    fn bind_round_trips_nulls_formats_and_values() {
1482        let bind = Bind {
1483            portal: Bytes::from_static(b"portal"),
1484            statement: Bytes::from_static(b"statement"),
1485            parameter_formats: vec![1, 0],
1486            parameters: vec![None, Some(Bytes::from_static(b"value"))],
1487            result_formats: vec![1],
1488        };
1489        let frame = bind.to_frame().expect("valid Bind");
1490        assert_eq!(
1491            Frontend::decode(frame).expect("decodable Bind"),
1492            FrontendMessage::Bind(bind)
1493        );
1494    }
1495
1496    #[test]
1497    fn row_description_round_trips_all_metadata() {
1498        let description = RowDescription {
1499            fields: vec![FieldDescription {
1500                name: Bytes::from_static(b"answer"),
1501                table_oid: 16_384,
1502                column: 2,
1503                type_oid: 23,
1504                type_size: 4,
1505                type_modifier: -1,
1506                format: 1,
1507            }],
1508        };
1509        let frame = description.to_frame().expect("valid RowDescription");
1510        assert_eq!(
1511            Backend::decode(frame).expect("decodable RowDescription"),
1512            BackendMessage::RowDescription(description)
1513        );
1514    }
1515
1516    #[test]
1517    fn frontend_message_family_round_trips_structurally() {
1518        let messages = vec![
1519            FrontendMessage::Close(Close {
1520                target: DescribeTarget::Portal,
1521                name: Bytes::from_static(b"p"),
1522            }),
1523            FrontendMessage::Execute(Execute {
1524                portal: Bytes::from_static(b"p"),
1525                max_rows: 10,
1526            }),
1527            FrontendMessage::FunctionCall(FunctionCall {
1528                function_oid: 42,
1529                argument_formats: vec![1],
1530                arguments: vec![Some(Bytes::from_static(b"arg")), None],
1531                result_format: 1,
1532            }),
1533            FrontendMessage::Query(Bytes::from_static(b"select 1")),
1534            FrontendMessage::Flush,
1535            FrontendMessage::Sync,
1536            FrontendMessage::Terminate,
1537            FrontendMessage::CopyData(Bytes::from_static(b"row\n")),
1538            FrontendMessage::CopyDone,
1539            FrontendMessage::CopyFail(Bytes::from_static(b"cancelled")),
1540            FrontendMessage::PasswordResponse(Bytes::from_static(b"opaque response")),
1541        ];
1542        for message in messages {
1543            let frame = message
1544                .to_frame()
1545                .expect("reconstructable frontend message");
1546            assert_eq!(
1547                Frontend::decode(frame).expect("decodable frontend message"),
1548                message
1549            );
1550        }
1551    }
1552
1553    #[test]
1554    fn backend_message_family_round_trips_structurally() {
1555        let diagnostic = DiagnosticResponse {
1556            fields: vec![
1557                DiagnosticField {
1558                    code: b'S',
1559                    value: Bytes::from_static(b"ERROR"),
1560                },
1561                DiagnosticField {
1562                    code: b'M',
1563                    value: Bytes::from_static(b"rewritable message"),
1564                },
1565            ],
1566        };
1567        let copy = CopyResponse {
1568            overall_format: 0,
1569            column_formats: vec![0, 1],
1570        };
1571        let messages = vec![
1572            BackendMessage::ParseComplete,
1573            BackendMessage::BindComplete,
1574            BackendMessage::CloseComplete,
1575            BackendMessage::CommandComplete(Bytes::from_static(b"SELECT 1")),
1576            BackendMessage::CopyData(Bytes::from_static(b"row\n")),
1577            BackendMessage::CopyDone,
1578            BackendMessage::CopyInResponse(copy.clone()),
1579            BackendMessage::CopyOutResponse(copy.clone()),
1580            BackendMessage::CopyBothResponse(copy),
1581            BackendMessage::DataRow(DataRow {
1582                columns: vec![Some(Bytes::from_static(b"42")), None],
1583            }),
1584            BackendMessage::EmptyQueryResponse,
1585            BackendMessage::ErrorResponse(diagnostic.clone()),
1586            BackendMessage::NoticeResponse(diagnostic),
1587            BackendMessage::NoData,
1588            BackendMessage::ParameterDescription(vec![23, 25]),
1589            BackendMessage::PortalSuspended,
1590            BackendMessage::FunctionCallResponse(Bytes::from_static(b"result")),
1591        ];
1592        for message in messages {
1593            let frame = message.to_frame().expect("reconstructable backend message");
1594            assert_eq!(
1595                Backend::decode(frame).expect("decodable backend message"),
1596                message
1597            );
1598        }
1599    }
1600}