Skip to main content

mz_deploy/cli/commands/
sql.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Sql command - launch an interactive psql session using the active profile.
11
12use std::io;
13use std::os::unix::process::CommandExt;
14use std::process::Command;
15
16use crate::cli::CliError;
17use crate::client::{build_options_string, default_sslmode};
18use crate::config::Settings;
19
20const APPLICATION_NAME: &str = "mz-deploy-sql";
21
22/// Launch an interactive `psql` session connected via the active profile.
23///
24/// Translates the profile into `PG*` environment variables, then replaces the
25/// current process with `psql`. Trailing arguments are forwarded to `psql`
26/// unchanged, so callers can pass flags like `-c "SELECT 1"` or `-f script.sql`.
27///
28/// Unlike the rest of the CLI, this command does **not** pin the session to
29/// `_mz_deploy_server`: interactive shells should use whatever cluster the
30/// profile (or server default) selects.
31pub fn run(settings: &Settings, psql_args: Vec<String>) -> Result<(), CliError> {
32    let profile = settings.connection();
33    let host = profile.require_host()?;
34
35    let mut cmd = Command::new("psql");
36    cmd.env("PGHOST", host);
37    cmd.env("PGPORT", profile.port.to_string());
38    cmd.env("PGUSER", &profile.username);
39    cmd.env("PGDATABASE", "materialize");
40    if let Some(password) = &profile.password {
41        cmd.env("PGPASSWORD", password);
42    }
43
44    let mode = profile.sslmode.unwrap_or_else(|| default_sslmode(host));
45    cmd.env("PGSSLMODE", mode.libpq_name());
46    if let Some(cert) = &profile.sslrootcert {
47        cmd.env("PGSSLROOTCERT", cert);
48    }
49
50    if let Some(options) = build_options_string(&profile.options) {
51        cmd.env("PGOPTIONS", options);
52    }
53
54    cmd.env("PGAPPNAME", APPLICATION_NAME);
55    cmd.args(psql_args);
56
57    // `exec` replaces the current process on success and only returns on
58    // failure. Map ENOENT to an install hint, since a missing `psql` binary
59    // is the most common error here.
60    let err = cmd.exec();
61    let msg = if err.kind() == io::ErrorKind::NotFound {
62        "failed to launch psql: binary not found on PATH. \
63         Install it with `brew install libpq` (macOS) or \
64         `apt install postgresql-client` (Debian/Ubuntu)."
65            .to_string()
66    } else {
67        format!("failed to launch psql: {err}")
68    };
69    Err(CliError::Message(msg))
70}