M4
This commit is contained in:
+45
-10
@@ -107,6 +107,11 @@ impl Pg {
|
||||
if self.running() {
|
||||
return Ok(());
|
||||
}
|
||||
// Another install's Postgres on the shared port would leave only "exit code 1" in the log.
|
||||
if crate::stack::port_taken(PG_PORT) {
|
||||
return Err("Another ODIAN install is already running on this PC: it holds the database port. Stop it in the launcher window that started it, then try again."
|
||||
.to_string());
|
||||
}
|
||||
let mut cmd = self.tool("pg_ctl");
|
||||
cmd
|
||||
.arg("start")
|
||||
@@ -151,6 +156,34 @@ impl Pg {
|
||||
Self::run(cmd, "psql")
|
||||
}
|
||||
|
||||
/// Run a script on the `odian` database through psql's stdin and return what it printed
|
||||
/// (unaligned, tuples only). For sync, whose JSON payloads are too large for a command line.
|
||||
pub fn script(&self, sql: &str) -> Result<String, String> {
|
||||
use std::io::Write;
|
||||
let port = PG_PORT.to_string();
|
||||
let mut cmd = self.tool("psql");
|
||||
cmd
|
||||
.env("PGCLIENTENCODING", "UTF8")
|
||||
.args(["-X", "-q", "-A", "-t", "-v", "ON_ERROR_STOP=1", "-h", "127.0.0.1", "-p", port.as_str()])
|
||||
.args(["-U", PG_USER, "-d", PG_DATABASE, "-f", "-"])
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
let mut child = cmd.spawn().map_err(|e| format!("cannot run psql: {e}"))?;
|
||||
let mut stdin = child.stdin.take().ok_or("cannot write to psql")?;
|
||||
// Feed from a thread: psql filling its stdout pipe while we still write would deadlock.
|
||||
let body = sql.as_bytes().to_vec();
|
||||
let writer = std::thread::spawn(move || stdin.write_all(&body));
|
||||
let out = child.wait_with_output().map_err(|e| format!("psql failed: {e}"))?;
|
||||
let _ = writer.join();
|
||||
if out.status.success() {
|
||||
return Ok(String::from_utf8_lossy(&out.stdout).into_owned());
|
||||
}
|
||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||
let tail: String = stderr.chars().rev().take(1500).collect::<Vec<_>>().into_iter().rev().collect();
|
||||
Err(format!("psql failed ({}): {}", out.status, tail.trim()))
|
||||
}
|
||||
|
||||
/// Replace the `odian` database with a snapshot. Only for a PC whose restore never
|
||||
/// completed: anything an operator did in the old database is dropped.
|
||||
pub fn restore(
|
||||
@@ -225,24 +258,26 @@ impl Pg {
|
||||
.args(["-h", "127.0.0.1", "-p", port.as_str(), "-U", PG_USER, "-d", PG_DATABASE])
|
||||
.args(["--no-owner", "--no-privileges", "--exit-on-error"]);
|
||||
if pre_data {
|
||||
cmd.arg("-L").arg(self.pre_data_list(&dump)?);
|
||||
// A fresh database already has schema public; --exit-on-error would stop on the duplicate.
|
||||
let drop = |line: &str| line.contains(" SCHEMA - public ") || line.contains(" COMMENT - SCHEMA public ");
|
||||
cmd.arg("-L").arg(self.toc_without(&dump, "pre-data.list", &drop)?);
|
||||
} else if file.section == "post-data" {
|
||||
// A DHP running sync has marking triggers on its tables; they call sync.mark(), which
|
||||
// this database gets only when the launcher loads the version's sync schema.
|
||||
let drop = |line: &str| line.contains(" TRIGGER ") && line.contains(" sync_mark_");
|
||||
cmd.arg("-L").arg(self.toc_without(&dump, "post-data.list", &drop)?);
|
||||
}
|
||||
cmd.arg(&dump);
|
||||
Self::run(cmd, &format!("pg_restore {}", file.path)).map(|_| ())
|
||||
}
|
||||
|
||||
/// The pre-data table of contents without `CREATE SCHEMA public`: a fresh database
|
||||
/// already has that schema, and --exit-on-error would stop on the duplicate.
|
||||
fn pre_data_list(&self, dump: &Path) -> Result<PathBuf, String> {
|
||||
/// The dump's table of contents without the entries `drop` names, for `pg_restore -L`.
|
||||
fn toc_without(&self, dump: &Path, list_name: &str, drop: &dyn Fn(&str) -> bool) -> Result<PathBuf, String> {
|
||||
let mut cmd = self.tool("pg_restore");
|
||||
cmd.arg("-l").arg(dump);
|
||||
let listing = Self::run(cmd, "pg_restore -l")?;
|
||||
let kept: String = listing
|
||||
.lines()
|
||||
.filter(|line| !(line.contains(" SCHEMA - public ") || line.contains(" COMMENT - SCHEMA public ")))
|
||||
.map(|line| format!("{line}\n"))
|
||||
.collect();
|
||||
let path = self.logs.join("pre-data.list");
|
||||
let kept: String = listing.lines().filter(|line| !drop(line)).map(|line| format!("{line}\n")).collect();
|
||||
let path = self.logs.join(list_name);
|
||||
std::fs::write(&path, kept).map_err(|e| format!("cannot write {}: {e}", path.display()))?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user