sqlx_postgres/options/
mod.rs

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