Skip to main content

sqlx_postgres/options/
mod.rs

1use std::borrow::Cow;
2use std::env::var;
3use std::fmt::{self, Display, Write};
4use std::path::{Path, PathBuf};
5
6pub use ssl_mode::PgSslMode;
7
8use crate::{connection::LogSettings, net::tls::CertificateInput};
9
10mod connect;
11mod parse;
12mod pgpass;
13mod ssl_mode;
14
15#[doc = include_str!("doc.md")]
16#[derive(Debug, Clone)]
17pub struct PgConnectOptions {
18    pub(crate) host: String,
19    pub(crate) host_addr: Option<String>,
20    pub(crate) port: u16,
21    pub(crate) socket: Option<PathBuf>,
22    pub(crate) username: String,
23    pub(crate) password: Option<String>,
24    pub(crate) database: Option<String>,
25    pub(crate) ssl_mode: PgSslMode,
26    pub(crate) ssl_root_cert: Option<CertificateInput>,
27    pub(crate) ssl_client_cert: Option<CertificateInput>,
28    pub(crate) ssl_client_key: Option<CertificateInput>,
29    pub(crate) statement_cache_capacity: usize,
30    pub(crate) application_name: Option<String>,
31    pub(crate) log_settings: LogSettings,
32    pub(crate) extra_float_digits: Option<Cow<'static, str>>,
33    pub(crate) options: Option<String>,
34}
35
36impl Default for PgConnectOptions {
37    fn default() -> Self {
38        Self::new_without_pgpass().apply_pgpass()
39    }
40}
41
42impl PgConnectOptions {
43    /// Create a default set of connection options populated from the current environment.
44    ///
45    /// This behaves as if parsed from the connection string `postgres://`
46    ///
47    /// See the type-level documentation for details.
48    pub fn new() -> Self {
49        Self::new_without_pgpass().apply_pgpass()
50    }
51
52    /// Create a default set of connection options _without_ reading from `passfile`.
53    ///
54    /// Equivalent to [`PgConnectOptions::new()`] but `passfile` is ignored.
55    ///
56    /// See the type-level documentation for details.
57    pub fn new_without_pgpass() -> Self {
58        let port = var("PGPORT")
59            .ok()
60            .and_then(|v| v.parse().ok())
61            .unwrap_or(5432);
62
63        let host = var("PGHOST").ok().unwrap_or_else(|| default_host(port));
64
65        let host_addr = var("PGHOSTADDR").ok();
66
67        let username = if let Ok(username) = var("PGUSER") {
68            username
69        } else if let Ok(username) = whoami::username() {
70            username
71        } else {
72            // keep the same fallback as previous version
73            "unknown".to_string()
74        };
75
76        let database = var("PGDATABASE").ok();
77
78        PgConnectOptions {
79            port,
80            host,
81            host_addr,
82            socket: None,
83            username,
84            password: var("PGPASSWORD").ok(),
85            database,
86            ssl_root_cert: var("PGSSLROOTCERT").ok().map(CertificateInput::from),
87            ssl_client_cert: var("PGSSLCERT").ok().map(CertificateInput::from),
88            // As of writing, the implementation of `From<String>` only looks for
89            // `-----BEGIN CERTIFICATE-----` and so will not attempt to parse
90            // a PEM-encoded private key.
91            ssl_client_key: var("PGSSLKEY").ok().map(CertificateInput::from),
92            ssl_mode: var("PGSSLMODE")
93                .ok()
94                .and_then(|v| v.parse().ok())
95                .unwrap_or_default(),
96            statement_cache_capacity: 100,
97            application_name: var("PGAPPNAME").ok(),
98            extra_float_digits: Some("2".into()),
99            log_settings: Default::default(),
100            options: var("PGOPTIONS").ok(),
101        }
102    }
103
104    pub(crate) fn apply_pgpass(mut self) -> Self {
105        if self.password.is_none() {
106            self.password = pgpass::load_password(
107                &self.host,
108                self.port,
109                &self.username,
110                self.database.as_deref(),
111            );
112        }
113
114        self
115    }
116
117    /// Sets the name of the host to connect to.
118    ///
119    /// If a host name begins with a slash, it specifies
120    /// Unix-domain communication rather than TCP/IP communication; the value is the name of
121    /// the directory in which the socket file is stored.
122    ///
123    /// The default behavior when host is not specified, or is empty,
124    /// is to connect to a Unix-domain socket
125    ///
126    /// # Example
127    ///
128    /// ```rust
129    /// # use sqlx_postgres::PgConnectOptions;
130    /// let options = PgConnectOptions::new()
131    ///     .host("localhost");
132    /// ```
133    pub fn host(mut self, host: &str) -> Self {
134        host.clone_into(&mut self.host);
135        self
136    }
137
138    /// Sets the host address to connect to.
139    ///
140    /// This is different to the host parameter as it overwrites DNS lookups for TCP/IP
141    /// communication. This is particuarly useful when the DB port has to be
142    /// proxied to localhost for security reasons.
143    ///
144    /// # Example
145    ///
146    /// ```rust
147    /// # use sqlx_postgres::PgConnectOptions;
148    /// let options = PgConnectOptions::new()
149    ///     .host("example.com");
150    ///
151    /// // Initially, host_addr should be None (unless PGHOSTADDR env var is set)
152    /// // For this test, we assume it's not set
153    /// assert_eq!(options.get_host_addr(), None);
154    ///
155    /// let options = options.host_addr("127.0.0.1");
156    ///
157    /// // After setting, host_addr should contain the specified value
158    /// assert_eq!(options.get_host_addr(), Some("127.0.0.1"));
159    /// ```
160    pub fn host_addr(mut self, host_addr: &str) -> Self {
161        self.host_addr = Some(host_addr.to_string());
162        self
163    }
164
165    /// Sets the port to connect to at the server host.
166    ///
167    /// The default port for PostgreSQL is `5432`.
168    ///
169    /// # Example
170    ///
171    /// ```rust
172    /// # use sqlx_postgres::PgConnectOptions;
173    /// let options = PgConnectOptions::new()
174    ///     .port(5432);
175    /// ```
176    pub fn port(mut self, port: u16) -> Self {
177        self.port = port;
178        self
179    }
180
181    /// Sets a custom path to a directory containing a unix domain socket,
182    /// switching the connection method from TCP to the corresponding socket.
183    ///
184    /// By default set to `None`.
185    pub fn socket(mut self, path: impl AsRef<Path>) -> Self {
186        self.socket = Some(path.as_ref().to_path_buf());
187        self
188    }
189
190    /// Sets the username to connect as.
191    ///
192    /// Defaults to be the same as the operating system name of
193    /// the user running the application.
194    ///
195    /// # Example
196    ///
197    /// ```rust
198    /// # use sqlx_postgres::PgConnectOptions;
199    /// let options = PgConnectOptions::new()
200    ///     .username("postgres");
201    /// ```
202    pub fn username(mut self, username: &str) -> Self {
203        username.clone_into(&mut self.username);
204        self
205    }
206
207    /// Sets the password to use if the server demands password authentication.
208    ///
209    /// # Example
210    ///
211    /// ```rust
212    /// # use sqlx_postgres::PgConnectOptions;
213    /// let options = PgConnectOptions::new()
214    ///     .username("root")
215    ///     .password("safe-and-secure");
216    /// ```
217    pub fn password(mut self, password: &str) -> Self {
218        self.password = Some(password.to_owned());
219        self
220    }
221
222    /// Sets the database name. Defaults to be the same as the user name.
223    ///
224    /// # Example
225    ///
226    /// ```rust
227    /// # use sqlx_postgres::PgConnectOptions;
228    /// let options = PgConnectOptions::new()
229    ///     .database("postgres");
230    /// ```
231    pub fn database(mut self, database: &str) -> Self {
232        self.database = Some(database.to_owned());
233        self
234    }
235
236    /// Sets whether or with what priority a secure SSL TCP/IP connection will be negotiated
237    /// with the server.
238    ///
239    /// By default, the SSL mode is [`Prefer`](PgSslMode::Prefer), and the client will
240    /// first attempt an SSL connection but fallback to a non-SSL connection on failure.
241    ///
242    /// Ignored for Unix domain socket communication.
243    ///
244    /// # Example
245    ///
246    /// ```rust
247    /// # use sqlx_postgres::{PgSslMode, PgConnectOptions};
248    /// let options = PgConnectOptions::new()
249    ///     .ssl_mode(PgSslMode::Require);
250    /// ```
251    pub fn ssl_mode(mut self, mode: PgSslMode) -> Self {
252        self.ssl_mode = mode;
253        self
254    }
255
256    /// Sets the name of a file containing SSL certificate authority (CA) certificate(s).
257    /// If the file exists, the server's certificate will be verified to be signed by
258    /// one of these authorities.
259    ///
260    /// # Example
261    ///
262    /// ```rust
263    /// # use sqlx_postgres::{PgSslMode, PgConnectOptions};
264    /// let options = PgConnectOptions::new()
265    ///     // Providing a CA certificate with less than VerifyCa is pointless
266    ///     .ssl_mode(PgSslMode::VerifyCa)
267    ///     .ssl_root_cert("./ca-certificate.crt");
268    /// ```
269    pub fn ssl_root_cert(mut self, cert: impl AsRef<Path>) -> Self {
270        self.ssl_root_cert = Some(CertificateInput::File(cert.as_ref().to_path_buf()));
271        self
272    }
273
274    /// Sets the name of a file containing SSL client certificate.
275    ///
276    /// # Example
277    ///
278    /// ```rust
279    /// # use sqlx_postgres::{PgSslMode, PgConnectOptions};
280    /// let options = PgConnectOptions::new()
281    ///     // Providing a CA certificate with less than VerifyCa is pointless
282    ///     .ssl_mode(PgSslMode::VerifyCa)
283    ///     .ssl_client_cert("./client.crt");
284    /// ```
285    pub fn ssl_client_cert(mut self, cert: impl AsRef<Path>) -> Self {
286        self.ssl_client_cert = Some(CertificateInput::File(cert.as_ref().to_path_buf()));
287        self
288    }
289
290    /// Sets the SSL client certificate as a PEM-encoded byte slice.
291    ///
292    /// This should be an ASCII-encoded blob that starts with `-----BEGIN CERTIFICATE-----`.
293    ///
294    /// # Example
295    /// Note: embedding SSL certificates and keys in the binary is not advised.
296    /// This is for illustration purposes only.
297    ///
298    /// ```rust
299    /// # use sqlx_postgres::{PgSslMode, PgConnectOptions};
300    ///
301    /// const CERT: &[u8] = b"\
302    /// -----BEGIN CERTIFICATE-----
303    /// <Certificate data here.>
304    /// -----END CERTIFICATE-----";
305    ///    
306    /// let options = PgConnectOptions::new()
307    ///     // Providing a CA certificate with less than VerifyCa is pointless
308    ///     .ssl_mode(PgSslMode::VerifyCa)
309    ///     .ssl_client_cert_from_pem(CERT);
310    /// ```
311    pub fn ssl_client_cert_from_pem(mut self, cert: impl AsRef<[u8]>) -> Self {
312        self.ssl_client_cert = Some(CertificateInput::Inline(cert.as_ref().to_vec()));
313        self
314    }
315
316    /// Sets the name of a file containing SSL client key.
317    ///
318    /// # Example
319    ///
320    /// ```rust
321    /// # use sqlx_postgres::{PgSslMode, PgConnectOptions};
322    /// let options = PgConnectOptions::new()
323    ///     // Providing a CA certificate with less than VerifyCa is pointless
324    ///     .ssl_mode(PgSslMode::VerifyCa)
325    ///     .ssl_client_key("./client.key");
326    /// ```
327    pub fn ssl_client_key(mut self, key: impl AsRef<Path>) -> Self {
328        self.ssl_client_key = Some(CertificateInput::File(key.as_ref().to_path_buf()));
329        self
330    }
331
332    /// Sets the SSL client key as a PEM-encoded byte slice.
333    ///
334    /// This should be an ASCII-encoded blob that starts with `-----BEGIN PRIVATE KEY-----`.
335    ///
336    /// # Example
337    /// Note: embedding SSL certificates and keys in the binary is not advised.
338    /// This is for illustration purposes only.
339    ///
340    /// ```rust
341    /// # use sqlx_postgres::{PgSslMode, PgConnectOptions};
342    ///
343    /// const KEY: &[u8] = b"\
344    /// -----BEGIN PRIVATE KEY-----
345    /// <Private key data here.>
346    /// -----END PRIVATE KEY-----";
347    ///
348    /// let options = PgConnectOptions::new()
349    ///     // Providing a CA certificate with less than VerifyCa is pointless
350    ///     .ssl_mode(PgSslMode::VerifyCa)
351    ///     .ssl_client_key_from_pem(KEY);
352    /// ```
353    pub fn ssl_client_key_from_pem(mut self, key: impl AsRef<[u8]>) -> Self {
354        self.ssl_client_key = Some(CertificateInput::Inline(key.as_ref().to_vec()));
355        self
356    }
357
358    /// Sets PEM encoded trusted SSL Certificate Authorities (CA).
359    ///
360    /// # Example
361    ///
362    /// ```rust
363    /// # use sqlx_postgres::{PgSslMode, PgConnectOptions};
364    /// let options = PgConnectOptions::new()
365    ///     // Providing a CA certificate with less than VerifyCa is pointless
366    ///     .ssl_mode(PgSslMode::VerifyCa)
367    ///     .ssl_root_cert_from_pem(vec![]);
368    /// ```
369    pub fn ssl_root_cert_from_pem(mut self, pem_certificate: Vec<u8>) -> Self {
370        self.ssl_root_cert = Some(CertificateInput::Inline(pem_certificate));
371        self
372    }
373
374    /// Sets the capacity of the connection's statement cache in a number of stored
375    /// distinct statements. Caching is handled using LRU, meaning when the
376    /// amount of queries hits the defined limit, the oldest statement will get
377    /// dropped.
378    ///
379    /// The default cache capacity is 100 statements.
380    pub fn statement_cache_capacity(mut self, capacity: usize) -> Self {
381        self.statement_cache_capacity = capacity;
382        self
383    }
384
385    /// Sets the application name. Defaults to None
386    ///
387    /// # Example
388    ///
389    /// ```rust
390    /// # use sqlx_postgres::PgConnectOptions;
391    /// let options = PgConnectOptions::new()
392    ///     .application_name("my-app");
393    /// ```
394    pub fn application_name(mut self, application_name: &str) -> Self {
395        self.application_name = Some(application_name.to_owned());
396        self
397    }
398
399    /// Sets or removes the `extra_float_digits` connection option.
400    ///
401    /// This changes the default precision of floating-point values returned in text mode (when
402    /// not using prepared statements such as calling methods of [`Executor`] directly).
403    ///
404    /// Historically, Postgres would by default round floating-point values to 6 and 15 digits
405    /// for `float4`/`REAL` (`f32`) and `float8`/`DOUBLE` (`f64`), respectively, which would mean
406    /// that the returned value may not be exactly the same as its representation in Postgres.
407    ///
408    /// The nominal range for this value is `-15` to `3`, where negative values for this option
409    /// cause floating-points to be rounded to that many fewer digits than normal (`-1` causes
410    /// `float4` to be rounded to 5 digits instead of six, or 14 instead of 15 for `float8`),
411    /// positive values cause Postgres to emit that many extra digits of precision over default
412    /// (or simply use maximum precision in Postgres 12 and later),
413    /// and 0 means keep the default behavior (or the "old" behavior described above
414    /// as of Postgres 12).
415    ///
416    /// SQLx sets this value to 3 by default, which tells Postgres to return floating-point values
417    /// at their maximum precision in the hope that the parsed value will be identical to its
418    /// counterpart in Postgres. This is also the default in Postgres 12 and later anyway.
419    ///
420    /// However, older versions of Postgres and alternative implementations that talk the Postgres
421    /// protocol may not support this option, or the full range of values.
422    ///
423    /// If you get an error like "unknown option `extra_float_digits`" when connecting, try
424    /// setting this to `None` or consult the manual of your database for the allowed range
425    /// of values.
426    ///
427    /// For more information, see:
428    /// * [Postgres manual, 20.11.2: Client Connection Defaults; Locale and Formatting][20.11.2]
429    /// * [Postgres manual, 8.1.3: Numeric Types; Floating-point Types][8.1.3]
430    ///
431    /// [`Executor`]: crate::executor::Executor
432    /// [20.11.2]: https://www.postgresql.org/docs/current/runtime-config-client.html#RUNTIME-CONFIG-CLIENT-FORMAT
433    /// [8.1.3]: https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-FLOAT
434    ///
435    /// ### Examples
436    /// ```rust
437    /// # use sqlx_postgres::PgConnectOptions;
438    ///
439    /// let mut options = PgConnectOptions::new()
440    ///     // for Redshift and Postgres 10
441    ///     .extra_float_digits(2);
442    ///
443    /// let mut options = PgConnectOptions::new()
444    ///     // don't send the option at all (Postgres 9 and older)
445    ///     .extra_float_digits(None);
446    /// ```
447    pub fn extra_float_digits(mut self, extra_float_digits: impl Into<Option<i8>>) -> Self {
448        self.extra_float_digits = extra_float_digits.into().map(|it| it.to_string().into());
449        self
450    }
451
452    /// Set additional startup options for the connection as a list of key-value pairs.
453    ///
454    /// Escapes the options’ backslash and space characters as per
455    /// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-OPTIONS
456    ///
457    /// # Example
458    ///
459    /// ```rust
460    /// # use sqlx_postgres::PgConnectOptions;
461    /// let options = PgConnectOptions::new()
462    ///     .options([("geqo", "off"), ("statement_timeout", "5min")]);
463    /// ```
464    pub fn options<K, V, I>(mut self, options: I) -> Self
465    where
466        K: Display,
467        V: Display,
468        I: IntoIterator<Item = (K, V)>,
469    {
470        // Do this in here so `options_str` is only set if we have an option to insert
471        let options_str = self.options.get_or_insert_with(String::new);
472        for (k, v) in options {
473            if !options_str.is_empty() {
474                options_str.push(' ');
475            }
476
477            options_str.push_str("-c ");
478            write!(PgOptionsWriteEscaped(options_str), "{k}={v}").ok();
479        }
480        self
481    }
482
483    /// We try using a socket if hostname starts with `/` or if socket parameter
484    /// is specified.
485    pub(crate) fn fetch_socket(&self) -> Option<String> {
486        match self.socket {
487            Some(ref socket) => {
488                let full_path = format!("{}/.s.PGSQL.{}", socket.display(), self.port);
489                Some(full_path)
490            }
491            None if self.host.starts_with('/') => {
492                let full_path = format!("{}/.s.PGSQL.{}", self.host, self.port);
493                Some(full_path)
494            }
495            _ => None,
496        }
497    }
498}
499
500impl PgConnectOptions {
501    /// Get the current host.
502    ///
503    /// # Example
504    ///
505    /// ```rust
506    /// # use sqlx_postgres::PgConnectOptions;
507    /// let options = PgConnectOptions::new()
508    ///     .host("127.0.0.1");
509    /// assert_eq!(options.get_host(), "127.0.0.1");
510    /// ```
511    pub fn get_host(&self) -> &str {
512        &self.host
513    }
514
515    /// Get the current host addr.
516    ///
517    /// # Example
518    ///
519    /// ```rust
520    /// # use sqlx_postgres::PgConnectOptions;
521    /// let options = PgConnectOptions::new()
522    ///     .host("example.com");
523    ///
524    /// // Initially, host_addr should be None (unless PGHOSTADDR env var is set)
525    /// // For this test, we assume it's not set
526    /// assert_eq!(options.get_host_addr(), None);
527    ///
528    /// let options = options.host_addr("127.0.0.1");
529    ///
530    /// // After setting host_addr, it should return the configured value
531    /// assert_eq!(options.get_host_addr(), Some("127.0.0.1"));
532    /// ```
533    pub fn get_host_addr(&self) -> Option<&str> {
534        self.host_addr.as_deref()
535    }
536
537    /// Get the server's port.
538    ///
539    /// # Example
540    ///
541    /// ```rust
542    /// # use sqlx_postgres::PgConnectOptions;
543    /// let options = PgConnectOptions::new()
544    ///     .port(6543);
545    /// assert_eq!(options.get_port(), 6543);
546    /// ```
547    pub fn get_port(&self) -> u16 {
548        self.port
549    }
550
551    /// Get the socket path.
552    ///
553    /// # Example
554    ///
555    /// ```rust
556    /// # use sqlx_postgres::PgConnectOptions;
557    /// let options = PgConnectOptions::new()
558    ///     .socket("/tmp");
559    /// assert!(options.get_socket().is_some());
560    /// ```
561    pub fn get_socket(&self) -> Option<&PathBuf> {
562        self.socket.as_ref()
563    }
564
565    /// Get the server's port.
566    ///
567    /// # Example
568    ///
569    /// ```rust
570    /// # use sqlx_postgres::PgConnectOptions;
571    /// let options = PgConnectOptions::new()
572    ///     .username("foo");
573    /// assert_eq!(options.get_username(), "foo");
574    /// ```
575    pub fn get_username(&self) -> &str {
576        &self.username
577    }
578
579    /// Get the current database name.
580    ///
581    /// # Example
582    ///
583    /// ```rust
584    /// # use sqlx_postgres::PgConnectOptions;
585    /// let options = PgConnectOptions::new()
586    ///     .database("postgres");
587    /// assert!(options.get_database().is_some());
588    /// ```
589    pub fn get_database(&self) -> Option<&str> {
590        self.database.as_deref()
591    }
592
593    /// Get the SSL mode.
594    ///
595    /// # Example
596    ///
597    /// ```rust
598    /// # use sqlx_postgres::{PgConnectOptions, PgSslMode};
599    /// let options = PgConnectOptions::new();
600    /// assert!(matches!(options.get_ssl_mode(), PgSslMode::Prefer));
601    /// ```
602    pub fn get_ssl_mode(&self) -> PgSslMode {
603        self.ssl_mode
604    }
605
606    /// Get the application name.
607    ///
608    /// # Example
609    ///
610    /// ```rust
611    /// # use sqlx_postgres::PgConnectOptions;
612    /// let options = PgConnectOptions::new()
613    ///     .application_name("service");
614    /// assert!(options.get_application_name().is_some());
615    /// ```
616    pub fn get_application_name(&self) -> Option<&str> {
617        self.application_name.as_deref()
618    }
619
620    /// Get the options.
621    ///
622    /// # Example
623    ///
624    /// ```rust
625    /// # use sqlx_postgres::PgConnectOptions;
626    /// let options = PgConnectOptions::new()
627    ///     .options([("foo", "bar")]);
628    /// assert!(options.get_options().is_some());
629    /// ```
630    pub fn get_options(&self) -> Option<&str> {
631        self.options.as_deref()
632    }
633}
634
635fn default_host(port: u16) -> String {
636    // try to check for the existence of a unix socket and uses that
637    let socket = format!(".s.PGSQL.{port}");
638    let candidates = [
639        "/var/run/postgresql", // Debian
640        "/private/tmp",        // OSX (homebrew)
641        "/tmp",                // Default
642    ];
643
644    for candidate in &candidates {
645        if Path::new(candidate).join(&socket).exists() {
646            return candidate.to_string();
647        }
648    }
649
650    // fallback to localhost if no socket was found
651    "localhost".to_owned()
652}
653
654/// Writer that escapes passed-in PostgreSQL options.
655///
656/// Escapes backslashes and spaces with an additional backslash according to
657/// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-OPTIONS
658#[derive(Debug)]
659struct PgOptionsWriteEscaped<'a>(&'a mut String);
660
661impl Write for PgOptionsWriteEscaped<'_> {
662    fn write_str(&mut self, s: &str) -> fmt::Result {
663        let mut span_start = 0;
664
665        for (span_end, matched) in s.match_indices([' ', '\\']) {
666            write!(self.0, r"{}\{matched}", &s[span_start..span_end])?;
667            span_start = span_end + matched.len();
668        }
669
670        // Write the rest of the string after the last match, or all of it if no matches
671        self.0.push_str(&s[span_start..]);
672
673        Ok(())
674    }
675
676    fn write_char(&mut self, ch: char) -> fmt::Result {
677        if matches!(ch, ' ' | '\\') {
678            self.0.push('\\');
679        }
680
681        self.0.push(ch);
682
683        Ok(())
684    }
685}
686
687#[test]
688fn test_options_formatting() {
689    let options = PgConnectOptions::new().options([("geqo", "off")]);
690    assert_eq!(options.options, Some("-c geqo=off".to_string()));
691    let options = options.options([("search_path", "sqlx")]);
692    assert_eq!(
693        options.options,
694        Some("-c geqo=off -c search_path=sqlx".to_string())
695    );
696    let options = PgConnectOptions::new().options([("geqo", "off"), ("statement_timeout", "5min")]);
697    assert_eq!(
698        options.options,
699        Some("-c geqo=off -c statement_timeout=5min".to_string())
700    );
701    // https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-OPTIONS
702    let options =
703        PgConnectOptions::new().options([("application_name", r"/back\slash/ and\ spaces")]);
704    assert_eq!(
705        options.options,
706        Some(r"-c application_name=/back\\slash/\ and\\\ spaces".to_string())
707    );
708    let options = PgConnectOptions::new();
709    assert_eq!(options.options, None);
710}
711
712#[test]
713fn test_pg_write_escaped() {
714    let mut buf = String::new();
715    let mut x = PgOptionsWriteEscaped(&mut buf);
716    x.write_str("x").unwrap();
717    x.write_str("").unwrap();
718    x.write_char('\\').unwrap();
719    x.write_str("y \\").unwrap();
720    x.write_char(' ').unwrap();
721    x.write_char('z').unwrap();
722    assert_eq!(buf, r"x\\y\ \\\ z");
723}