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