sqlx_postgres/message/
startup.rs

1use crate::io::PgBufMutExt;
2use crate::io::{BufMutExt, ProtocolEncode};
3
4// To begin a session, a frontend opens a connection to the server and sends a startup message.
5// This message includes the names of the user and of the database the user wants to connect to;
6// it also identifies the particular protocol version to be used.
7
8// Optionally, the startup message can include additional settings for run-time parameters.
9
10pub struct Startup<'a> {
11    /// The database user name to connect as. Required; there is no default.
12    pub username: Option<&'a str>,
13
14    /// The database to connect to. Defaults to the user name.
15    pub database: Option<&'a str>,
16
17    /// Additional start-up params.
18    /// <https://www.postgresql.org/docs/devel/runtime-config-client.html>
19    pub params: &'a [(&'a str, &'a str)],
20}
21
22// Startup cannot impl FrontendMessage because it doesn't have a format code.
23impl ProtocolEncode<'_> for Startup<'_> {
24    fn encode_with(&self, buf: &mut Vec<u8>, _context: ()) -> Result<(), crate::Error> {
25        buf.reserve(120);
26
27        buf.put_length_prefixed(|buf| {
28            // The protocol version number. The most significant 16 bits are the
29            // major version number (3 for the protocol described here). The least
30            // significant 16 bits are the minor version number (0
31            // for the protocol described here)
32            buf.extend(&196_608_i32.to_be_bytes());
33
34            if let Some(username) = self.username {
35                // The database user name to connect as.
36                encode_startup_param(buf, "user", username);
37            }
38
39            if let Some(database) = self.database {
40                // The database to connect to. Defaults to the user name.
41                encode_startup_param(buf, "database", database);
42            }
43
44            for (name, value) in self.params {
45                encode_startup_param(buf, name, value);
46            }
47
48            // A zero byte is required as a terminator
49            // after the last name/value pair.
50            buf.push(0);
51
52            Ok(())
53        })
54    }
55}
56
57#[inline]
58fn encode_startup_param(buf: &mut Vec<u8>, name: &str, value: &str) {
59    buf.put_str_nul(name);
60    buf.put_str_nul(value);
61}
62
63#[test]
64fn test_encode_startup() {
65    const EXPECTED: &[u8] = b"\0\0\0)\0\x03\0\0user\0postgres\0database\0postgres\0\0";
66
67    let mut buf = Vec::new();
68    let m = Startup {
69        username: Some("postgres"),
70        database: Some("postgres"),
71        params: &[],
72    };
73
74    m.encode(&mut buf).unwrap();
75
76    assert_eq!(buf, EXPECTED);
77}
78
79#[cfg(all(test, not(debug_assertions)))]
80#[bench]
81fn bench_encode_startup(b: &mut test::Bencher) {
82    use test::black_box;
83
84    let mut buf = Vec::with_capacity(128);
85
86    b.iter(|| {
87        buf.clear();
88
89        black_box(Startup {
90            username: Some("postgres"),
91            database: Some("postgres"),
92            params: &[],
93        })
94        .encode(&mut buf);
95    });
96}