Skip to main content

postgresql_protocol/
backend.rs

1//! Backend (server → client) message decoding.
2//!
3//! Row payloads are returned as zero-copy [`Bytes`] slices of the receive
4//! buffer, so a `DataRow` never copies its column data.
5
6use bytes::{Buf, Bytes, BytesMut};
7use small_collections::SmallString;
8
9use crate::{
10    error::{DbError, Error},
11    oid::Oid,
12};
13
14/// Description of one result column.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct FieldDescription {
17    /// Column name.
18    pub name: String,
19    /// OID of the source table, or `0` for an expression.
20    pub table_oid: u32,
21    /// Attribute number within the source table, or `0`.
22    pub column_attr: i16,
23    /// Column type OID.
24    pub type_oid: Oid,
25    /// Type size in bytes, or negative for variable length.
26    pub type_size: i16,
27    /// Type modifier (e.g. numeric precision/scale), or `-1`.
28    pub type_mod: i32,
29    /// Wire format (`0` text, `1` binary).
30    pub format: i16,
31}
32
33impl FieldDescription {
34    /// `true` when this column originates from a real table column.
35    pub fn is_table_column(&self) -> bool {
36        self.table_oid != 0 && self.column_attr > 0
37    }
38}
39
40/// A decoded backend message.
41#[derive(Debug)]
42pub enum BackendMessage {
43    /// Authentication succeeded.
44    AuthenticationOk,
45    /// Server requests a cleartext password.
46    AuthenticationCleartextPassword,
47    /// Server requests an MD5 password, with a 4-byte salt.
48    AuthenticationMd5Password([u8; 4]),
49    /// Server offers SASL mechanisms.
50    AuthenticationSasl(Vec<String>),
51    /// SASL challenge.
52    AuthenticationSaslContinue(Bytes),
53    /// SASL completion.
54    AuthenticationSaslFinal(Bytes),
55    /// Backend process id and secret key, for query cancellation.
56    BackendKeyData {
57        /// Backend process ID.
58        pid: i32,
59        /// Cancellation secret key.
60        secret_key: i32,
61    },
62    /// A run-time parameter (e.g. `server_version`, `integer_datetimes`).
63    ParameterStatus {
64        /// Parameter name.
65        name: String,
66        /// Parameter value.
67        value: String,
68    },
69    /// The connection is ready for a new query.
70    ReadyForQuery(u8),
71    /// Description of the columns a statement or portal will return.
72    RowDescription(Vec<FieldDescription>),
73    /// The parameter types a prepared statement expects.
74    ParameterDescription(Vec<Oid>),
75    /// One result row; the payload starts with the column count.
76    DataRow(Bytes),
77    /// A statement completed, with its command tag.
78    CommandComplete(Bytes),
79    /// A `Parse` completed.
80    ParseComplete,
81    /// A `Bind` completed.
82    BindComplete,
83    /// A `Close` completed.
84    CloseComplete,
85    /// The statement/portal returns no rows.
86    NoData,
87    /// A portal was suspended by the row limit.
88    PortalSuspended,
89    /// An empty query string produced no command.
90    EmptyQueryResponse,
91    /// The server reported an error for the current request.
92    ErrorResponse(DbError),
93    /// A non-fatal notice, to be logged.
94    NoticeResponse(DbError),
95    /// An asynchronous `NOTIFY`.
96    NotificationResponse {
97        /// Notifying backend process ID.
98        pid: i32,
99        /// Channel name.
100        channel: String,
101        /// Notification payload.
102        payload: String,
103    },
104    /// The server is ready to receive `COPY ... FROM STDIN` data.
105    CopyInResponse {
106        /// Overall format (`0` text, `1` binary).
107        format: u8,
108        /// Per-column formats.
109        column_formats: Vec<i16>,
110    },
111    /// The server will stream `COPY ... TO STDOUT` data.
112    CopyOutResponse {
113        /// Overall format (`0` text, `1` binary).
114        format: u8,
115        /// Per-column formats.
116        column_formats: Vec<i16>,
117    },
118    /// A chunk of `COPY` data.
119    CopyData(Bytes),
120    /// The server finished streaming `COPY` data.
121    CopyDone,
122    /// The server advertises protocol version negotiation.
123    NegotiateProtocolVersion {
124        /// Newest supported minor protocol version.
125        newest_minor: i32,
126        /// Unrecognized startup options.
127        unsupported: Vec<String>,
128    },
129}
130
131/// Default largest accepted length for a single backend message body, in
132/// bytes (128 MiB).
133///
134/// A server (or a man in the middle, since `sslmode=require` does not
135/// authenticate the server) can otherwise make the client buffer up to 2 GiB
136/// while waiting for a message that will never arrive. The limit is checked
137/// before anything is buffered, so a hostile length fails immediately.
138///
139/// This can be raised or lowered per connection with
140/// [`decode_with_limit`]; see [`crate::error`] callers. 128 MiB is large enough
141/// for any realistic row while keeping the worst-case buffering bounded.
142pub const DEFAULT_MAX_MESSAGE_LEN: usize = 128 * 1024 * 1024;
143
144/// Attempts to decode one backend message from `buf` with the default size
145/// limit ([`DEFAULT_MAX_MESSAGE_LEN`]).
146///
147/// Returns `Ok(None)` when the buffer does not yet contain a full message.
148///
149/// # Errors
150///
151/// Returns [`Error::Decode`] when the message is malformed: an impossible
152/// length, a truncated body, an unterminated string or an invalid count.
153pub fn decode(buf: &mut BytesMut) -> Result<Option<BackendMessage>, Error> {
154    decode_with_limit(buf, DEFAULT_MAX_MESSAGE_LEN)
155}
156
157/// Like [`decode`], but rejects any message whose declared body length exceeds
158/// `max` bytes before anything is buffered.
159///
160/// `max` is clamped to at most `i32::MAX` (the largest length expressible on
161/// the wire).
162///
163/// # Errors
164///
165/// Returns [`Error::Decode`] when the message is malformed or larger than
166/// `max`.
167pub fn decode_with_limit(buf: &mut BytesMut, max: usize) -> Result<Option<BackendMessage>, Error> {
168    let max = max.min(i32::MAX as usize);
169    if buf.len() < 5 {
170        return Ok(None);
171    }
172    let tag = buf[0];
173    let len = i32::from_be_bytes([buf[1], buf[2], buf[3], buf[4]]);
174    if len < 4 {
175        return Err(Error::Decode(format!("invalid message length {len}")));
176    }
177    // `len` is now known to be positive, so the conversion is exact.
178    let len = usize::try_from(len).map_err(|_| Error::Decode(format!("invalid message length {len}")))?;
179    if len > max {
180        return Err(Error::Decode(format!("message length {len} exceeds the {max} byte limit")));
181    }
182    let total = 1 + len;
183    if buf.len() < total {
184        return Ok(None);
185    }
186
187    let frame = buf.split_to(total).freeze();
188    let payload = frame.slice(5..);
189    parse(tag, payload).map(Some)
190}
191
192fn parse(tag: u8, payload: Bytes) -> Result<BackendMessage, Error> {
193    match tag {
194        b'R' => {
195            let mut r = Reader::new(payload);
196            let kind = r.i32()?;
197            match kind {
198                0 => Ok(BackendMessage::AuthenticationOk),
199                3 => Ok(BackendMessage::AuthenticationCleartextPassword),
200                5 => {
201                    let salt = r.bytes(4)?;
202                    Ok(BackendMessage::AuthenticationMd5Password([salt[0], salt[1], salt[2], salt[3]]))
203                }
204                10 => {
205                    // Each mechanism is at least one byte plus its NUL.
206                    let mut mechanisms = Vec::with_capacity(r.cap(2, 2));
207                    while r.remaining() > 0 {
208                        let m = r.cstr()?;
209                        if m.is_empty() {
210                            break;
211                        }
212                        mechanisms.push(String::from_utf8_lossy(&m).into_owned());
213                    }
214                    Ok(BackendMessage::AuthenticationSasl(mechanisms))
215                }
216                11 => Ok(BackendMessage::AuthenticationSaslContinue(r.rest())),
217                12 => Ok(BackendMessage::AuthenticationSaslFinal(r.rest())),
218                other => Err(Error::Auth(format!("unsupported authentication method {other}"))),
219            }
220        }
221        b'K' => {
222            let mut r = Reader::new(payload);
223            Ok(BackendMessage::BackendKeyData {
224                pid: r.i32()?,
225                secret_key: r.i32()?,
226            })
227        }
228        b'S' => {
229            let mut r = Reader::new(payload);
230            Ok(BackendMessage::ParameterStatus {
231                name: r.string()?,
232                value: r.string()?,
233            })
234        }
235        b'Z' => {
236            let mut r = Reader::new(payload);
237            Ok(BackendMessage::ReadyForQuery(r.u8()?))
238        }
239        b'T' => {
240            let mut r = Reader::new(payload);
241            let count = r.count16()?;
242            // A field description is at least 19 bytes.
243            let mut fields = Vec::with_capacity(r.cap(count, 19));
244            for _ in 0..count {
245                fields.push(FieldDescription {
246                    name: r.string()?,
247                    table_oid: r.u32()?,
248                    column_attr: r.i16()?,
249                    type_oid: r.u32()?,
250                    type_size: r.i16()?,
251                    type_mod: r.i32()?,
252                    format: r.i16()?,
253                });
254            }
255            Ok(BackendMessage::RowDescription(fields))
256        }
257        b't' => {
258            let mut r = Reader::new(payload);
259            let count = r.count16()?;
260            let mut oids = Vec::with_capacity(r.cap(count, 4));
261            for _ in 0..count {
262                oids.push(r.u32()?);
263            }
264            Ok(BackendMessage::ParameterDescription(oids))
265        }
266        b'D' => Ok(BackendMessage::DataRow(payload)),
267        b'C' => {
268            let mut r = Reader::new(payload);
269            Ok(BackendMessage::CommandComplete(r.cstr()?))
270        }
271        b'1' => Ok(BackendMessage::ParseComplete),
272        b'2' => Ok(BackendMessage::BindComplete),
273        b'3' => Ok(BackendMessage::CloseComplete),
274        b'n' => Ok(BackendMessage::NoData),
275        b's' => Ok(BackendMessage::PortalSuspended),
276        b'I' => Ok(BackendMessage::EmptyQueryResponse),
277        b'E' => {
278            let err = decode_error(payload)?;
279            Err(Error::Server(Box::new(err)))
280        }
281        b'N' => {
282            let err = decode_error(payload)?;
283            Ok(BackendMessage::NoticeResponse(err))
284        }
285        b'A' => {
286            let mut r = Reader::new(payload);
287            Ok(BackendMessage::NotificationResponse {
288                pid: r.i32()?,
289                channel: r.string()?,
290                payload: r.string()?,
291            })
292        }
293        b'G' => {
294            let mut r = Reader::new(payload);
295            let format = r.u8()?;
296            let count = r.count16()?;
297            let mut column_formats = Vec::with_capacity(r.cap(count, 2));
298            for _ in 0..count {
299                column_formats.push(r.i16()?);
300            }
301            Ok(BackendMessage::CopyInResponse {
302                format,
303                column_formats,
304            })
305        }
306        b'W' => Err(Error::Protocol(
307            "CopyBothResponse (streaming replication) is not supported".into(),
308        )),
309        b'H' => {
310            let mut r = Reader::new(payload);
311            let format = r.u8()?;
312            let count = r.count16()?;
313            let mut column_formats = Vec::with_capacity(r.cap(count, 2));
314            for _ in 0..count {
315                column_formats.push(r.i16()?);
316            }
317            Ok(BackendMessage::CopyOutResponse {
318                format,
319                column_formats,
320            })
321        }
322        b'd' => Ok(BackendMessage::CopyData(payload)),
323        b'c' => Ok(BackendMessage::CopyDone),
324        b'v' => {
325            let mut r = Reader::new(payload);
326            let newest_minor = r.i32()?;
327            let count = r.count32()?;
328            let mut unsupported = Vec::with_capacity(r.cap(count, 1));
329            for _ in 0..count {
330                unsupported.push(r.string()?);
331            }
332            Ok(BackendMessage::NegotiateProtocolVersion {
333                newest_minor,
334                unsupported,
335            })
336        }
337        other => Err(Error::Protocol(format!("unknown message tag {:?}", other as char))),
338    }
339}
340
341fn decode_error(payload: Bytes) -> Result<DbError, Error> {
342    let mut r = Reader::new(payload);
343    let mut err = DbError::default();
344    while r.remaining() > 0 {
345        let field = r.u8()?;
346        if field == 0 {
347            break;
348        }
349        match field {
350            // Short, bounded fields stay inline and never allocate.
351            b'S' | b'V' => err.severity = r.small_string_lossy::<8>()?,
352            b'C' => err.code = r.small_string_lossy::<5>()?,
353            b'M' => err.message = r.string_lossy()?,
354            b'D' => err.detail = Some(r.string_lossy()?),
355            b'H' => err.hint = Some(r.string_lossy()?),
356            b'P' => err.position = r.string_lossy()?.parse().ok(),
357            b'n' => err.constraint = Some(r.string_lossy()?),
358            b't' => err.table = Some(r.string_lossy()?),
359            b'c' => err.column = Some(r.string_lossy()?),
360            b's' => err.schema = Some(r.string_lossy()?),
361            b'd' => err.data_type = Some(r.string_lossy()?),
362            // Unknown fields still have to be consumed.
363            _ => {
364                let _ = r.string_lossy()?;
365            }
366        }
367    }
368    Ok(err)
369}
370
371/// A checked reader over a message payload.
372struct Reader {
373    buf: Bytes,
374}
375
376impl Reader {
377    fn new(buf: Bytes) -> Self {
378        Reader {
379            buf,
380        }
381    }
382
383    fn remaining(&self) -> usize {
384        self.buf.len()
385    }
386
387    fn rest(self) -> Bytes {
388        self.buf
389    }
390
391    fn u8(&mut self) -> Result<u8, Error> {
392        if self.buf.len() < 1 {
393            return Err(Error::Decode("unexpected end of message".into()));
394        }
395        Ok(self.buf.get_u8())
396    }
397
398    fn i16(&mut self) -> Result<i16, Error> {
399        if self.buf.len() < 2 {
400            return Err(Error::Decode("unexpected end of message".into()));
401        }
402        Ok(self.buf.get_i16())
403    }
404
405    fn i32(&mut self) -> Result<i32, Error> {
406        if self.buf.len() < 4 {
407            return Err(Error::Decode("unexpected end of message".into()));
408        }
409        Ok(self.buf.get_i32())
410    }
411
412    fn u32(&mut self) -> Result<u32, Error> {
413        Ok(self.i32()?.cast_unsigned())
414    }
415
416    /// Reads a 16-bit element count. Negative counts are rejected instead of
417    /// being reinterpreted as huge `usize` values.
418    fn count16(&mut self) -> Result<usize, Error> {
419        let n = self.i16()?;
420        usize::try_from(n).map_err(|_| Error::Decode(format!("invalid element count {n}")))
421    }
422
423    /// Reads a 32-bit element count. Negative counts are rejected instead of
424    /// being reinterpreted as huge `usize` values.
425    fn count32(&mut self) -> Result<usize, Error> {
426        let n = self.i32()?;
427        usize::try_from(n).map_err(|_| Error::Decode(format!("invalid element count {n}")))
428    }
429
430    /// Clamps a wire-supplied element count to what the remaining payload
431    /// could possibly hold, so a hostile count cannot request a huge
432    /// allocation up front.
433    fn cap(&self, count: usize, min_bytes_per_item: usize) -> usize {
434        count.min(self.buf.len() / min_bytes_per_item.max(1))
435    }
436
437    fn bytes(&mut self, n: usize) -> Result<Bytes, Error> {
438        if self.buf.len() < n {
439            return Err(Error::Decode("unexpected end of message".into()));
440        }
441        Ok(self.buf.split_to(n))
442    }
443
444    fn cstr(&mut self) -> Result<Bytes, Error> {
445        match self.buf.iter().position(|&b| b == 0) {
446            Some(pos) => {
447                let s = self.buf.split_to(pos);
448                self.buf.advance(1);
449                Ok(s)
450            }
451            None => Err(Error::Decode("unterminated string".into())),
452        }
453    }
454
455    fn string(&mut self) -> Result<String, Error> {
456        let bytes = self.cstr()?;
457        String::from_utf8(bytes.to_vec()).map_err(|e| Error::Decode(format!("invalid UTF-8: {e}")))
458    }
459
460    /// Like [`Reader::string`], but replaces invalid UTF-8 instead of erroring.
461    ///
462    /// Used for `ErrorResponse` / `NoticeResponse` fields: a server that
463    /// mis-encodes (or a hostile one) must not be able to hide its actual error
464    /// behind a decode failure in a field the client only reports.
465    fn string_lossy(&mut self) -> Result<String, Error> {
466        let bytes = self.cstr()?;
467        Ok(String::from_utf8_lossy(&bytes).into_owned())
468    }
469
470    /// Like [`Reader::string_lossy`], but stores the result in a [`SmallString`].
471    ///
472    /// Short fields (the SQLSTATE code, the severity word) then live inline and
473    /// never allocate; longer values — including a hostile server's oversized
474    /// fields — spill onto the heap exactly like [`Reader::string_lossy`].
475    fn small_string_lossy<const N: usize>(&mut self) -> Result<SmallString<N>, Error> {
476        let bytes = self.cstr()?;
477        Ok(SmallString::from_utf8_lossy(&bytes))
478    }
479}
480
481#[cfg(test)]
482mod tests {
483    use super::*;
484
485    fn message(tag: u8, payload: &[u8]) -> BytesMut {
486        let mut buf = BytesMut::new();
487        buf.extend_from_slice(&[tag]);
488        buf.extend_from_slice(&i32::try_from(payload.len() + 4).unwrap().to_be_bytes());
489        buf.extend_from_slice(payload);
490        buf
491    }
492
493    #[test]
494    fn incomplete_returns_none() {
495        let mut buf = BytesMut::from(&b"R"[..]);
496        assert!(decode(&mut buf).unwrap().is_none());
497    }
498
499    #[test]
500    fn decode_ready_for_query() {
501        let mut buf = message(b'Z', &[b'I']);
502        match decode(&mut buf).unwrap().unwrap() {
503            BackendMessage::ReadyForQuery(s) => assert_eq!(s, b'I'),
504            other => panic!("unexpected {other:?}"),
505        }
506    }
507
508    #[test]
509    fn decode_error_response() {
510        // S=ERROR\0 C=23505\0 M=dup\0 n=users_pkey\0 \0
511        let mut payload = Vec::new();
512        payload.extend_from_slice(b"SERROR\0");
513        payload.extend_from_slice(b"C23505\0");
514        payload.extend_from_slice(b"Mduplicate key\0");
515        payload.extend_from_slice(b"nusers_pkey\0");
516        payload.push(0);
517        let mut buf = message(b'E', &payload);
518        match decode(&mut buf).unwrap_err() {
519            Error::Server(err) => {
520                assert_eq!(err.code, "23505");
521                assert!(err.is_unique_violation());
522                assert_eq!(err.constraint.as_deref(), Some("users_pkey"));
523            }
524            other => panic!("unexpected {other:?}"),
525        }
526    }
527
528    #[test]
529    fn decode_row_description() {
530        let mut payload = Vec::new();
531        payload.extend_from_slice(&1i16.to_be_bytes());
532        payload.extend_from_slice(b"id\0");
533        payload.extend_from_slice(&0u32.to_be_bytes());
534        payload.extend_from_slice(&0i16.to_be_bytes());
535        payload.extend_from_slice(&23u32.to_be_bytes());
536        payload.extend_from_slice(&4i16.to_be_bytes());
537        payload.extend_from_slice(&(-1i32).to_be_bytes());
538        payload.extend_from_slice(&1i16.to_be_bytes());
539        let mut buf = message(b'T', &payload);
540        match decode(&mut buf).unwrap().unwrap() {
541            BackendMessage::RowDescription(fields) => {
542                assert_eq!(fields.len(), 1);
543                assert_eq!(fields[0].name, "id");
544                assert_eq!(fields[0].type_oid, 23);
545            }
546            other => panic!("unexpected {other:?}"),
547        }
548    }
549
550    #[test]
551    fn decode_parameter_description() {
552        let mut payload = Vec::new();
553        payload.extend_from_slice(&2i16.to_be_bytes());
554        payload.extend_from_slice(&23u32.to_be_bytes());
555        payload.extend_from_slice(&25u32.to_be_bytes());
556        let mut buf = message(b't', &payload);
557        match decode(&mut buf).unwrap().unwrap() {
558            BackendMessage::ParameterDescription(oids) => assert_eq!(oids, vec![23, 25]),
559            other => panic!("unexpected {other:?}"),
560        }
561    }
562
563    #[test]
564    fn negative_counts_are_rejected_not_panicking() {
565        for tag in [b'T', b't'] {
566            let mut p = Vec::new();
567            p.extend_from_slice(&(-1i16).to_be_bytes());
568            let mut buf = message(tag, &p);
569            let err = decode(&mut buf).unwrap_err();
570            assert!(matches!(err, Error::Decode(_)), "tag {}: {err:?}", tag as char);
571        }
572
573        // CopyIn/CopyOut responses.
574        let mut p = Vec::new();
575        p.push(1u8);
576        p.extend_from_slice(&(-3i16).to_be_bytes());
577        for tag in [b'G', b'W', b'H'] {
578            let mut buf = message(tag, &p);
579            assert!(decode(&mut buf).is_err());
580        }
581
582        // NegotiateProtocolVersion.
583        let mut p = Vec::new();
584        p.extend_from_slice(&3i32.to_be_bytes());
585        p.extend_from_slice(&(-1i32).to_be_bytes());
586        let mut buf = message(b'v', &p);
587        assert!(decode(&mut buf).is_err());
588    }
589
590    #[test]
591    fn huge_counts_do_not_allocate_the_payload_size() {
592        // 32767 claimed columns in a 2-byte payload: the decoder must not ask
593        // for a 32767-element allocation up front, and must fail cleanly.
594        let mut p = Vec::new();
595        p.extend_from_slice(&32767i16.to_be_bytes());
596        let mut buf = message(b'T', &p);
597        assert!(decode(&mut buf).is_err());
598
599        let mut p = Vec::new();
600        p.extend_from_slice(&1i32.to_be_bytes());
601        p.extend_from_slice(&i32::MAX.to_be_bytes());
602        let mut buf = message(b'v', &p);
603        assert!(decode(&mut buf).is_err());
604    }
605
606    #[test]
607    fn oversized_message_length_is_rejected() {
608        let mut buf = BytesMut::new();
609        buf.extend_from_slice(&[b'D']);
610        buf.extend_from_slice(&i32::MAX.to_be_bytes());
611        match decode(&mut buf) {
612            Err(Error::Decode(msg)) => assert!(msg.contains("exceeds"), "{msg}"),
613            other => panic!("unexpected {other:?}"),
614        }
615        // The declared length never has to be buffered before it is checked.
616        assert_eq!(buf.len(), 5);
617    }
618
619    #[test]
620    fn custom_limit_is_honoured() {
621        // A 1 KiB body is accepted by default but rejected with a small limit.
622        let payload = vec![0u8; 1024];
623        let mut buf = message(b'D', &payload);
624        assert!(decode(&mut buf).unwrap().is_some());
625
626        let mut buf = message(b'D', &payload);
627        match decode_with_limit(&mut buf, 100) {
628            Err(Error::Decode(msg)) => assert!(msg.contains("exceeds"), "{msg}"),
629            other => panic!("unexpected {other:?}"),
630        }
631        // The over-limit message is rejected before it is split off.
632        assert_eq!(buf.len(), 1 + 4 + payload.len());
633
634        // A limit at least as large as the message accepts it.
635        let mut buf = message(b'Z', &[b'I']);
636        assert!(decode_with_limit(&mut buf, 5).unwrap().is_some());
637
638        // A zero limit rejects every message rather than silently accepting.
639        let mut buf = message(b'Z', &[b'I']);
640        assert!(decode_with_limit(&mut buf, 0).is_err());
641    }
642
643    #[test]
644    fn truncated_payloads_error_instead_of_panicking() {
645        // Unterminated C strings.
646        let mut buf = message(b'S', b"name-without-nul");
647        assert!(decode(&mut buf).is_err());
648        let mut buf = message(b'C', b"INSERT 0 1");
649        assert!(decode(&mut buf).is_err());
650        // Not enough bytes for the fixed part.
651        let mut buf = message(b'K', &[0, 1]);
652        assert!(decode(&mut buf).is_err());
653        let mut buf = message(b'Z', &[]);
654        assert!(decode(&mut buf).is_err());
655        let mut buf = message(b'A', &[0, 0, 0, 1]);
656        assert!(decode(&mut buf).is_err());
657        // Row description with a field count but no fields.
658        let mut p = Vec::new();
659        p.extend_from_slice(&1i16.to_be_bytes());
660        let mut buf = message(b'T', &p);
661        assert!(decode(&mut buf).is_err());
662    }
663
664    #[test]
665    fn error_fields_may_be_non_utf8_or_non_ascii() {
666        // `class()` on the decoded error must not panic on such a code.
667        let mut payload = Vec::new();
668        payload.extend_from_slice(b"SERROR\0");
669        payload.extend_from_slice("C\u{20ac}\0".as_bytes());
670        payload.extend_from_slice(b"Mweird\0");
671        payload.push(0);
672        let mut buf = message(b'E', &payload);
673        match decode(&mut buf).unwrap_err() {
674            Error::Server(err) => {
675                assert_eq!(err.class(), "");
676                assert_eq!(err.message, "weird");
677            }
678            other => panic!("unexpected {other:?}"),
679        }
680    }
681
682    #[test]
683    fn invalid_utf8_in_identifiers_is_an_error() {
684        let mut payload = Vec::new();
685        payload.extend_from_slice(&1i16.to_be_bytes());
686        payload.extend_from_slice(&[0xff, 0xfe, 0]);
687        let mut buf = message(b'T', &payload);
688        assert!(decode(&mut buf).is_err());
689    }
690
691    #[test]
692    fn invalid_utf8_in_error_fields_is_lossy_not_a_decode_error() {
693        // A mis-encoded (or hostile) server must not hide its actual error
694        // behind a decode failure: the field is reported with replacement
695        // characters instead.
696        let mut payload = Vec::new();
697        payload.extend_from_slice(b"SERROR\0");
698        payload.extend_from_slice(b"C42601\0");
699        payload.extend_from_slice(&[b'M', 0xff, 0xfe, b'h', 0x00]);
700        payload.push(0);
701        let mut buf = message(b'E', &payload);
702        match decode(&mut buf).unwrap_err() {
703            Error::Server(err) => {
704                assert_eq!(err.code, "42601");
705                assert!(err.message.contains('h'), "message was {:?}", err.message);
706                assert!(err.message.contains('\u{fffd}'), "message was {:?}", err.message);
707            }
708            other => panic!("unexpected {other:?}"),
709        }
710    }
711
712    #[test]
713    fn copy_both_response_is_rejected() {
714        // 'W' is CopyBothResponse (streaming replication), which this client
715        // does not support; it must not be mistaken for CopyInResponse.
716        let mut payload = Vec::new();
717        payload.push(1u8);
718        payload.extend_from_slice(&1i16.to_be_bytes());
719        payload.extend_from_slice(&1i16.to_be_bytes());
720        let mut buf = message(b'W', &payload);
721        assert!(matches!(decode(&mut buf), Err(Error::Protocol(_))));
722    }
723
724    #[test]
725    fn data_row_is_zero_copy_slice_of_frame() {
726        let mut payload = Vec::new();
727        payload.extend_from_slice(&1i16.to_be_bytes());
728        payload.extend_from_slice(&3i32.to_be_bytes());
729        payload.extend_from_slice(&[7, 8, 9]);
730        let mut buf = message(b'D', &payload);
731        match decode(&mut buf).unwrap().unwrap() {
732            BackendMessage::DataRow(bytes) => {
733                assert_eq!(bytes.len(), payload.len());
734            }
735            other => panic!("unexpected {other:?}"),
736        }
737    }
738}