Skip to content

Commit

Permalink
Add -o option to pgtemp daemon for passing server configs
Browse files Browse the repository at this point in the history
Allows `PgTempDBBuilder::with_config_param` with pgtemp daemon
  • Loading branch information
boustrophedon committed May 1, 2024
1 parent 7d086b5 commit fadb7be
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 15 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
unreleased
----------
Add -o options to pgtemp daemon for postgresql server configs

0.2
---
Documentation updates

0.1.0
-----
Initial release
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ build:
test:
cargo test --tests --examples --all-features

# Run all tests with and without all features (excluding pg16 since github runners do not support it)
# Run all tests with and without all features
test-ci:
cargo test --tests --examples --no-default-features
cargo test --tests --examples --all-features
Expand All @@ -26,7 +26,8 @@ lint:
-A clippy::doc-markdown \
-A clippy::missing-panics-doc \
-A clippy::new-without-default \
-A clippy::expect-fun-call
-A clippy::expect-fun-call \
-A clippy::no_effect_underscore_binding # TODO: fixed in clippy, can remove eventually

# Generate docs
doc:
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ The pgtemp cli tool allows you to even more simply make temporary connections, a

pgtemp supports loading (and dumping, in the library) the database to/from [dumpfiles via `pg_dump`](https://www.postgresql.org/docs/current/backup-dump.html).

Note that the default postgres authentication configuration (`pg_hba.conf`) in most cases allows all local connections. Since pgtemp only allows you to make servers that listen on localhost, this means in most cases you do not need to provide a password to connect. You may set the server's `hba_file` parameter in `PgTempDBBuilder::with_config_param` or use the pgtemp daemon's `-o` flag to pass `hba_file` there.

# Requirements
You must install both the postgresql client and server packages. On Debian/Ubuntu, they are `postgresql postgresql-client`, on Fedora they are `postgresql postgresql-server`, and on Arch Linux they are `postgresql postgresql-libs`. Note also that Debian/Ubuntu install the standard postgres binaries into their own directory, so you must add them to your path. For an Ubuntu GitHub Actions runner, it looks like:

Expand Down
2 changes: 1 addition & 1 deletion TODO
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ await db.async_shutdown()

- .pgpass file instead of just writing password to file? only seems to be an issue for createdb and really at that point we could just execute with psql instead.

- support all builder options in cli (--persist, custom config params)
- support all builder options in cli (e.g. --persist)

- cache initial files from initdb or otherwise optimize all setup stuff

Expand Down
25 changes: 25 additions & 0 deletions src/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use tokio::signal::unix::{signal, SignalKind};
/// Contains the clap args struct
pub mod cli {
use clap::Parser;
use std::error::Error;
use std::path::PathBuf;

#[derive(Parser, Debug)]
Expand All @@ -32,10 +33,30 @@ pub mod cli {
/// The sql script to be loaded on startup
pub load_from: Option<PathBuf>,

#[arg(long, short = 'o', value_name = "KEY=VAL", value_parser = parse_key_val::<String, String>)]
/// PostgreSQL server configuration parameters in key=value format to pass on startup. May
/// be passed multiple times.
pub server_params: Vec<(String, String)>,

/// The postgres connection uri to be used by pgtemp clients.
/// E.g. postgresql://localhost:5432/mytestdb
pub connection_uri: String,
}

// from https://github.com/clap-rs/clap/blob/d681a81dd7f4d7ff71f2e65be26d8f90783f7b40/examples/typed-derive.rs#L47C1-L59C2
/// Parse a single key-value pair
fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
where
T: std::str::FromStr,
T::Err: Error + Send + Sync + 'static,
U: std::str::FromStr,
U::Err: Error + Send + Sync + 'static,
{
let pos = s
.find('=')
.ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?;
Ok((s[..pos].parse()?, s[pos + 1..].parse()?))
}
}

#[cfg(feature = "cli")]
Expand Down Expand Up @@ -65,6 +86,10 @@ impl PgTempDaemon {
if let Some(load_from) = args.load_from {
builder = builder.load_database(&load_from);
}
for (key, value) in args.server_params {
builder = builder.with_config_param(&key, &value);
eprintln!("{}={}", key, value);
}

let port = builder.get_port_or_set_random();
let single_mode = args.single;
Expand Down
15 changes: 3 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,18 +446,12 @@ impl PgTempDBBuilder {

/// Get user if set or return default
pub fn get_user(&self) -> String {
self.db_user
.as_ref()
.cloned()
.unwrap_or(String::from("postgres"))
self.db_user.clone().unwrap_or(String::from("postgres"))
}

/// Get password if set or return default
pub fn get_password(&self) -> String {
self.password
.as_ref()
.cloned()
.unwrap_or(String::from("password"))
self.password.clone().unwrap_or(String::from("password"))
}

/// Unlike the other getters, this getter will try to open a new socket to find an unused port,
Expand All @@ -471,10 +465,7 @@ impl PgTempDBBuilder {

/// Get dbname if set or return default
pub fn get_dbname(&self) -> String {
self.dbname
.as_ref()
.cloned()
.unwrap_or(String::from("postgres"))
self.dbname.clone().unwrap_or(String::from("postgres"))
}
}

Expand Down
21 changes: 21 additions & 0 deletions tests/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ async fn daemon_single_mode() {
single: true,
data_dir_prefix: Some(temp_prefix.path().into()),
load_from: None,
server_params: vec![("geqo".into(), "off".into()), ("jit".into(), "off".into())],
connection_uri: uri.to_string(),
};

Expand All @@ -110,6 +111,9 @@ async fn daemon_single_mode() {
.await
.expect("failed to connect to db");

// check the config params were set
check_config(&mut conn1).await;

// create table on conn 2
create_table(&mut conn2).await;

Expand All @@ -121,6 +125,23 @@ async fn daemon_single_mode() {
check_data(&mut conn2, "test").await;
}

async fn check_config(conn: &mut PgConn) {
// jit, ssl, and geqo are the shorted postgres config names but we can't turn on ssl
let rows = sqlx::query("SELECT name, setting from pg_settings WHERE name = 'jit' OR name = 'geqo' ORDER BY name ASC")
.fetch_all(conn)
.await
.expect("failed to get config settings");
assert_eq!(rows.len(), 2);

let row = &rows[1];
let geqo: &str = row.get(1);
assert_eq!(geqo, "off");

let row = &rows[0];
let jit: &str = row.get(1);
assert_eq!(jit, "off");
}

async fn create_table(conn: &mut PgConn) {
sqlx::query(
"
Expand Down

0 comments on commit fadb7be

Please sign in to comment.