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}