Skip to content

Commit 1713ea1

Browse files
committed
feat(postgres): set options through setting run-time parameters
1 parent c750c24 commit 1713ea1

File tree

4 files changed

+73
-13
lines changed

4 files changed

+73
-13
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ impl PgConnection {
4040
params.push(("application_name", application_name));
4141
}
4242

43-
if let Some(ref options) = options.options {
44-
params.push(("options", options));
43+
for (k, v) in options.options.iter() {
44+
params.push((k, v));
4545
}
4646

4747
stream

sqlx-core/src/postgres/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub use error::{PgDatabaseError, PgErrorPosition};
3232
pub use listener::{PgListener, PgNotification};
3333
pub use message::PgSeverity;
3434
pub use options::{PgConnectOptions, PgSslMode};
35+
pub(crate) use options::parse_options;
3536
pub use query_result::PgQueryResult;
3637
pub use row::PgRow;
3738
pub use statement::PgStatement;

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

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::collections::HashMap;
12
use std::env::var;
23
use std::path::{Path, PathBuf};
34

@@ -33,7 +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. |
36-
/// | `options` | `None` | The command-line options to send to the server at connection start. |
37+
/// | `options` | `None` | The runtime parameters to send to the server at connection start. |
3738
///
3839
/// The URI scheme designator can be either `postgresql://` or `postgres://`.
3940
/// Each of the URI parts is optional.
@@ -86,7 +87,7 @@ pub struct PgConnectOptions {
8687
pub(crate) statement_cache_capacity: usize,
8788
pub(crate) application_name: Option<String>,
8889
pub(crate) log_settings: LogSettings,
89-
pub(crate) options: Option<String>,
90+
pub(crate) options: HashMap<String, String>,
9091
}
9192

9293
impl Default for PgConnectOptions {
@@ -147,7 +148,9 @@ impl PgConnectOptions {
147148
statement_cache_capacity: 100,
148149
application_name: var("PGAPPNAME").ok(),
149150
log_settings: Default::default(),
150-
options: var("PGOPTIONS").ok(),
151+
options: var("PGOPTIONS")
152+
.and_then(|v| Ok(parse_options(&v).unwrap_or_default().into_iter().collect()))
153+
.unwrap_or_default(),
151154
}
152155
}
153156

@@ -329,10 +332,17 @@ impl PgConnectOptions {
329332
/// ```rust
330333
/// # use sqlx_core::postgres::PgConnectOptions;
331334
/// let options = PgConnectOptions::new()
332-
/// .options("-c geqo=off -c statement_timeout=5min");
335+
/// .options(&[("geqo", "off"), ("statement_timeout", "5min")]);
333336
/// ```
334-
pub fn options(mut self, options: &str) -> Self {
335-
self.options = Some(options.to_owned());
337+
pub fn options<K, V, I>(mut self, options: I) -> Self
338+
where
339+
K: ToString,
340+
V: ToString,
341+
I: IntoIterator<Item = (K, V)>,
342+
{
343+
for (k, v) in options {
344+
self.options.insert(k.to_string(), v.to_string());
345+
}
336346
self
337347
}
338348

@@ -353,6 +363,24 @@ impl PgConnectOptions {
353363
}
354364
}
355365

366+
/// Parse a libpq style options string
367+
pub(crate) fn parse_options(input: &str) -> Option<Vec<(String, String)>> {
368+
let mut options = Vec::new();
369+
for part in input.split(' ') {
370+
let part = part.trim();
371+
if part.is_empty() || part == "-c" {
372+
continue;
373+
}
374+
let pair = part.splitn(2, '=').collect::<Vec<_>>();
375+
if pair.len() != 2 {
376+
return None;
377+
}
378+
options.push((pair[0].to_string(), pair[1].to_string()));
379+
}
380+
381+
Some(options)
382+
}
383+
356384
fn default_host(port: u16) -> String {
357385
// try to check for the existence of a unix socket and uses that
358386
let socket = format!(".s.PGSQL.{}", port);

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

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::error::Error;
2-
use crate::postgres::PgConnectOptions;
2+
use crate::postgres::{parse_options, PgConnectOptions};
33
use percent_encoding::percent_decode_str;
44
use std::net::IpAddr;
55
use std::str::FromStr;
@@ -85,7 +85,17 @@ impl FromStr for PgConnectOptions {
8585

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

88-
"options" => options = options.options(&*value),
88+
"options" => {
89+
if let Some(value) = parse_options(&value) {
90+
options = options.options(value);
91+
}
92+
}
93+
94+
k if k.starts_with("options[") => {
95+
if let Some(key) = k.strip_prefix("options[").unwrap().strip_suffix(']') {
96+
options = options.options([(key, &*value)]);
97+
}
98+
}
8999

90100
_ => log::warn!("ignoring unrecognized connect parameter: {}={}", key, value),
91101
}
@@ -198,9 +208,30 @@ fn it_parses_socket_correctly_with_username_percent_encoded() {
198208
assert_eq!(Some("database"), opts.database.as_deref());
199209
}
200210
#[test]
201-
fn it_parses_options() {
202-
let uri = "postgres:///?options=-c%20synchronous_commit%3Doff%20--search_path%3Dpostgres";
211+
fn it_parses_libpq_options_correctly() {
212+
let uri = "postgres:///?options=-c%20synchronous_commit%3Doff%20-c%20search_path%3Dpostgres";
213+
let opts = PgConnectOptions::from_str(uri).unwrap();
214+
215+
assert_eq!(
216+
Some(&"off".to_string()),
217+
opts.options.get("synchronous_commit")
218+
);
219+
assert_eq!(
220+
Some(&"postgres".to_string()),
221+
opts.options.get("search_path")
222+
);
223+
}
224+
#[test]
225+
fn it_parses_sqlx_options_correctly() {
226+
let uri = "postgres:///?options[synchronous_commit]=off&options[search_path]=postgres";
203227
let opts = PgConnectOptions::from_str(uri).unwrap();
204228

205-
assert_eq!(Some("-c synchronous_commit=off --search_path=postgres"), opts.options.as_deref());
229+
assert_eq!(
230+
Some(&"off".to_string()),
231+
opts.options.get("synchronous_commit")
232+
);
233+
assert_eq!(
234+
Some(&"postgres".to_string()),
235+
opts.options.get("search_path")
236+
);
206237
}

0 commit comments

Comments
 (0)