Skip to content

Commit b3091b0

Browse files
authored
feat(postgres): add an option to specify extra options (#1539)
* feat(postgres): add an option to specify extra options ... ... this allow you to specify stuff like search path and statement timeouts etc. * feat(postgres): set options through setting run-time parameters * feat(postgres): use flat command-list instead of hashmap * feat(postgres): make the options taking parameters with `Display` trait
1 parent 32f1273 commit b3091b0

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed

sqlx-core/src/postgres/connection/establish.rs

+4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ impl PgConnection {
4040
params.push(("application_name", application_name));
4141
}
4242

43+
if let Some(ref options) = options.options {
44+
params.push(("options", options));
45+
}
46+
4347
stream
4448
.send(Startup {
4549
username: Some(&options.username),

sqlx-core/src/postgres/options/mod.rs

+32
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::env::var;
2+
use std::fmt::Display;
23
use std::path::{Path, PathBuf};
34

45
mod connect;
@@ -33,6 +34,7 @@ pub use ssl_mode::PgSslMode;
3334
/// | `password` | `None` | Password to be used if the server demands password authentication. |
3435
/// | `port` | `5432` | Port number to connect to at the server host, or socket file name extension for Unix-domain connections. |
3536
/// | `dbname` | `None` | The database name. |
37+
/// | `options` | `None` | The runtime parameters to send to the server at connection start. |
3638
///
3739
/// The URI scheme designator can be either `postgresql://` or `postgres://`.
3840
/// Each of the URI parts is optional.
@@ -85,6 +87,7 @@ pub struct PgConnectOptions {
8587
pub(crate) statement_cache_capacity: usize,
8688
pub(crate) application_name: Option<String>,
8789
pub(crate) log_settings: LogSettings,
90+
pub(crate) options: Option<String>,
8891
}
8992

9093
impl Default for PgConnectOptions {
@@ -145,6 +148,7 @@ impl PgConnectOptions {
145148
statement_cache_capacity: 100,
146149
application_name: var("PGAPPNAME").ok(),
147150
log_settings: Default::default(),
151+
options: var("PGOPTIONS").ok(),
148152
}
149153
}
150154

@@ -332,6 +336,34 @@ impl PgConnectOptions {
332336
self
333337
}
334338

339+
/// Set additional startup options for the connection as a list of key-value pairs.
340+
///
341+
/// # Example
342+
///
343+
/// ```rust
344+
/// # use sqlx_core::postgres::PgConnectOptions;
345+
/// let options = PgConnectOptions::new()
346+
/// .options([("geqo", "off"), ("statement_timeout", "5min")]);
347+
/// ```
348+
pub fn options<K, V, I>(mut self, options: I) -> Self
349+
where
350+
K: Display,
351+
V: Display,
352+
I: IntoIterator<Item = (K, V)>,
353+
{
354+
let mut options_str = String::new();
355+
for (k, v) in options {
356+
options_str += &format!("-c {}={}", k, v);
357+
}
358+
if let Some(ref mut v) = self.options {
359+
v.push(' ');
360+
v.push_str(&options_str);
361+
} else {
362+
self.options = Some(options_str);
363+
}
364+
self
365+
}
366+
335367
/// We try using a socket if hostname starts with `/` or if socket parameter
336368
/// is specified.
337369
pub(crate) fn fetch_socket(&self) -> Option<String> {

sqlx-core/src/postgres/options/parse.rs

+35
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,21 @@ impl FromStr for PgConnectOptions {
8585

8686
"application_name" => options = options.application_name(&*value),
8787

88+
"options" => {
89+
if let Some(options) = options.options.as_mut() {
90+
options.push(' ');
91+
options.push_str(&*value);
92+
} else {
93+
options.options = Some(value.to_string());
94+
}
95+
}
96+
97+
k if k.starts_with("options[") => {
98+
if let Some(key) = k.strip_prefix("options[").unwrap().strip_suffix(']') {
99+
options = options.options([(key, &*value)]);
100+
}
101+
}
102+
88103
_ => log::warn!("ignoring unrecognized connect parameter: {}={}", key, value),
89104
}
90105
}
@@ -197,3 +212,23 @@ fn it_parses_socket_correctly_with_username_percent_encoded() {
197212
assert_eq!(Some("/var/lib/postgres/".into()), opts.socket);
198213
assert_eq!(Some("database"), opts.database.as_deref());
199214
}
215+
#[test]
216+
fn it_parses_libpq_options_correctly() {
217+
let uri = "postgres:///?options=-c%20synchronous_commit%3Doff%20--search_path%3Dpostgres";
218+
let opts = PgConnectOptions::from_str(uri).unwrap();
219+
220+
assert_eq!(
221+
Some("-c synchronous_commit=off --search_path=postgres".into()),
222+
opts.options
223+
);
224+
}
225+
#[test]
226+
fn it_parses_sqlx_options_correctly() {
227+
let uri = "postgres:///?options[synchronous_commit]=off&options[search_path]=postgres";
228+
let opts = PgConnectOptions::from_str(uri).unwrap();
229+
230+
assert_eq!(
231+
Some("-c synchronous_commit=off -c search_path=postgres".into()),
232+
opts.options
233+
);
234+
}

0 commit comments

Comments
 (0)