summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2023-03-21 17:03:42 +0000
committerTom Lane2023-03-21 17:03:56 +0000
commitb0d8f2d983cb25d1035fae1cd7de214dd67809b4 (patch)
tree765a0ea3579a960f3574a38e229338011b804e3a
parent0f85db92b9ea167d3b9e90f3fb5fb3b9a93babc2 (diff)
Add SHELL_ERROR and SHELL_EXIT_CODE magic variables to psql.
These are set after a \! command or a backtick substitution. SHELL_ERROR is just "true" for error (nonzero exit status) or "false" for success, while SHELL_EXIT_CODE records the actual exit status following standard shell/system(3) conventions. Corey Huinker, reviewed by Maxim Orlov and myself Discussion: https://postgr.es/m/CADkLM=cWao2x2f+UDw15W1JkVFr_bsxfstw=NGea7r9m4j-7rQ@mail.gmail.com
-rw-r--r--doc/src/sgml/ref/psql-ref.sgml28
-rw-r--r--src/bin/psql/command.c15
-rw-r--r--src/bin/psql/help.c4
-rw-r--r--src/bin/psql/psqlscanslash.l24
-rw-r--r--src/common/wait_error.c19
-rw-r--r--src/include/port.h1
6 files changed, 88 insertions, 3 deletions
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 7b8ae9fac30..29bbec21886 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -4267,6 +4267,34 @@ bar
</listitem>
</varlistentry>
+ <varlistentry id="app-psql-variables-shell-error">
+ <term><varname>SHELL_ERROR</varname></term>
+ <listitem>
+ <para>
+ <literal>true</literal> if the last shell command
+ failed, <literal>false</literal> if it succeeded.
+ This applies to shell commands invoked via the <literal>\!</literal>
+ meta-command or backquote (<literal>`</literal>) expansion.
+ See also <varname>SHELL_EXIT_CODE</varname>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="app-psql-variables-shell-exit-code">
+ <term><varname>SHELL_EXIT_CODE</varname></term>
+ <listitem>
+ <para>
+ The exit status returned by the last shell command.
+ 0&ndash;127 represent program exit codes, 128&ndash;255
+ indicate termination by a signal, and -1 indicates failure
+ to launch a program or to collect its exit status.
+ This applies to shell commands invoked via the <literal>\!</literal>
+ meta-command or backquote (<literal>`</literal>) expansion.
+ See also <varname>SHELL_ERROR</varname>.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="app-psql-variables-show-all-results">
<term><varname>SHOW_ALL_RESULTS</varname></term>
<listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 61ec049f054..d7731234b63 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -5041,6 +5041,21 @@ do_shell(const char *command)
else
result = system(command);
+ if (result == 0)
+ {
+ SetVariable(pset.vars, "SHELL_EXIT_CODE", "0");
+ SetVariable(pset.vars, "SHELL_ERROR", "false");
+ }
+ else
+ {
+ int exit_code = wait_result_to_exit_code(result);
+ char buf[32];
+
+ snprintf(buf, sizeof(buf), "%d", exit_code);
+ SetVariable(pset.vars, "SHELL_EXIT_CODE", buf);
+ SetVariable(pset.vars, "SHELL_ERROR", "true");
+ }
+
if (result == 127 || result == -1)
{
pg_log_error("\\!: failed");
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index e45c4aaca5a..48fd51592aa 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -451,6 +451,10 @@ helpVariables(unsigned short int pager)
HELP0(" SERVER_VERSION_NAME\n"
" SERVER_VERSION_NUM\n"
" server's version (in short string or numeric format)\n");
+ HELP0(" SHELL_ERROR\n"
+ " true if the last shell command failed, false if it succeeded\n");
+ HELP0(" SHELL_EXIT_CODE\n"
+ " exit status of the last shell command\n");
HELP0(" SHOW_ALL_RESULTS\n"
" show all results of a combined query (\\;) instead of only the last\n");
HELP0(" SHOW_CONTEXT\n"
diff --git a/src/bin/psql/psqlscanslash.l b/src/bin/psql/psqlscanslash.l
index 8449ee1a52e..fa2d0b2a5f4 100644
--- a/src/bin/psql/psqlscanslash.l
+++ b/src/bin/psql/psqlscanslash.l
@@ -19,6 +19,8 @@
#include "postgres_fe.h"
#include "psqlscanslash.h"
+#include "settings.h"
+
#include "common/logging.h"
#include "fe_utils/conditional.h"
@@ -772,6 +774,7 @@ evaluate_backtick(PsqlScanState state)
PQExpBufferData cmd_output;
FILE *fd;
bool error = false;
+ int exit_code = 0;
char buf[512];
size_t result;
@@ -783,6 +786,7 @@ evaluate_backtick(PsqlScanState state)
{
pg_log_error("%s: %m", cmd);
error = true;
+ exit_code = -1;
}
if (!error)
@@ -800,10 +804,19 @@ evaluate_backtick(PsqlScanState state)
} while (!feof(fd));
}
- if (fd && pclose(fd) == -1)
+ if (fd)
{
- pg_log_error("%s: %m", cmd);
- error = true;
+ /*
+ * Although pclose's result always sets SHELL_EXIT_CODE, we
+ * historically have abandoned the backtick substitution only if it
+ * returns -1.
+ */
+ exit_code = pclose(fd);
+ if (exit_code == -1)
+ {
+ pg_log_error("%s: %m", cmd);
+ error = true;
+ }
}
if (PQExpBufferDataBroken(cmd_output))
@@ -826,5 +839,10 @@ evaluate_backtick(PsqlScanState state)
appendBinaryPQExpBuffer(output_buf, cmd_output.data, cmd_output.len);
}
+ /* And finally, set the shell error variables */
+ snprintf(buf, sizeof(buf), "%d", wait_result_to_exit_code(exit_code));
+ SetVariable(pset.vars, "SHELL_EXIT_CODE", buf);
+ SetVariable(pset.vars, "SHELL_ERROR", (exit_code == 0) ? "false" : "true");
+
termPQExpBuffer(&cmd_output);
}
diff --git a/src/common/wait_error.c b/src/common/wait_error.c
index 4a3c3c61af1..a90b745f077 100644
--- a/src/common/wait_error.c
+++ b/src/common/wait_error.c
@@ -127,3 +127,22 @@ wait_result_is_any_signal(int exit_status, bool include_command_not_found)
return true;
return false;
}
+
+/*
+ * Return the shell exit code (normally 0 to 255) that corresponds to the
+ * given wait status. The argument is a wait status as returned by wait(2)
+ * or waitpid(2), which also applies to pclose(3) and system(3). To support
+ * the latter two cases, we pass through "-1" unchanged.
+ */
+int
+wait_result_to_exit_code(int exit_status)
+{
+ if (exit_status == -1)
+ return -1; /* failure of pclose() or system() */
+ if (WIFEXITED(exit_status))
+ return WEXITSTATUS(exit_status);
+ if (WIFSIGNALED(exit_status))
+ return 128 + WTERMSIG(exit_status);
+ /* On many systems, this is unreachable */
+ return -1;
+}
diff --git a/src/include/port.h b/src/include/port.h
index e66193bed9f..a88d403483e 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -495,6 +495,7 @@ extern char *escape_single_quotes_ascii(const char *src);
extern char *wait_result_to_str(int exitstatus);
extern bool wait_result_is_signal(int exit_status, int signum);
extern bool wait_result_is_any_signal(int exit_status, bool include_command_not_found);
+extern int wait_result_to_exit_code(int exit_status);
/*
* Interfaces that we assume all Unix system have. We retain individual macros