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