sqlx_postgres/message/
bind.rs

1use crate::io::{PgBufMutExt, PortalId, StatementId};
2use crate::message::{FrontendMessage, FrontendMessageFormat};
3use crate::PgValueFormat;
4use std::num::Saturating;
5
6/// <https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-BIND>
7///
8/// ## Note:
9///
10/// The integer values for number of bind parameters, number of parameter format codes,
11/// and number of result format codes all are interpreted as *unsigned*!
12#[derive(Debug)]
13pub struct Bind<'a> {
14    /// The ID of the destination portal (`PortalId::UNNAMED` selects the unnamed portal).
15    pub portal: PortalId,
16
17    /// The id of the source prepared statement.
18    pub statement: StatementId,
19
20    /// The parameter format codes. Each must presently be zero (text) or one (binary).
21    ///
22    /// There can be zero to indicate that there are no parameters or that the parameters all use the
23    /// default format (text); or one, in which case the specified format code is applied to all
24    /// parameters; or it can equal the actual number of parameters.
25    pub formats: &'a [PgValueFormat],
26
27    // Note: interpreted as unsigned, as is `formats.len()` and `result_formats.len()`
28    /// The number of parameters.
29    ///
30    /// May be different from `formats.len()`
31    pub num_params: u16,
32
33    /// The value of each parameter, in the indicated format.
34    pub params: &'a [u8],
35
36    /// The result-column format codes. Each must presently be zero (text) or one (binary).
37    ///
38    /// There can be zero to indicate that there are no result columns or that the
39    /// result columns should all use the default format (text); or one, in which
40    /// case the specified format code is applied to all result columns (if any);
41    /// or it can equal the actual number of result columns of the query.
42    pub result_formats: &'a [PgValueFormat],
43}
44
45impl FrontendMessage for Bind<'_> {
46    const FORMAT: FrontendMessageFormat = FrontendMessageFormat::Bind;
47
48    fn body_size_hint(&self) -> Saturating<usize> {
49        let mut size = Saturating(0);
50        size += self.portal.name_len();
51        size += self.statement.name_len();
52
53        // Parameter formats and length prefix
54        size += 2;
55        size += self.formats.len();
56
57        // `num_params`
58        size += 2;
59
60        size += self.params.len();
61
62        // Result formats and length prefix
63        size += 2;
64        size += self.result_formats.len();
65
66        size
67    }
68
69    fn encode_body(&self, buf: &mut Vec<u8>) -> Result<(), crate::Error> {
70        buf.put_portal_name(self.portal);
71
72        buf.put_statement_name(self.statement);
73
74        // NOTE: the integer values for the number of parameters and format codes in this message
75        // are all interpreted as *unsigned*!
76        //
77        // https://github.com/launchbadge/sqlx/issues/3464
78        let formats_len = u16::try_from(self.formats.len()).map_err(|_| {
79            err_protocol!("too many parameter format codes ({})", self.formats.len())
80        })?;
81
82        buf.extend(formats_len.to_be_bytes());
83
84        for &format in self.formats {
85            buf.extend((format as i16).to_be_bytes());
86        }
87
88        buf.extend(self.num_params.to_be_bytes());
89
90        buf.extend(self.params);
91
92        let result_formats_len = u16::try_from(self.formats.len())
93            .map_err(|_| err_protocol!("too many result format codes ({})", self.formats.len()))?;
94
95        buf.extend(result_formats_len.to_be_bytes());
96
97        for &format in self.result_formats {
98            buf.extend((format as i16).to_be_bytes());
99        }
100
101        Ok(())
102    }
103}
104
105// TODO: Unit Test Bind
106// TODO: Benchmark Bind