mirror of
https://github.com/postgres/postgres.git
synced 2025-06-30 21:42:05 +03:00
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
This commit is contained in:
@ -4267,6 +4267,34 @@ bar
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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–127 represent program exit codes, 128–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">
|
<varlistentry id="app-psql-variables-show-all-results">
|
||||||
<term><varname>SHOW_ALL_RESULTS</varname></term>
|
<term><varname>SHOW_ALL_RESULTS</varname></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -5041,6 +5041,21 @@ do_shell(const char *command)
|
|||||||
else
|
else
|
||||||
result = system(command);
|
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)
|
if (result == 127 || result == -1)
|
||||||
{
|
{
|
||||||
pg_log_error("\\!: failed");
|
pg_log_error("\\!: failed");
|
||||||
|
@ -451,6 +451,10 @@ helpVariables(unsigned short int pager)
|
|||||||
HELP0(" SERVER_VERSION_NAME\n"
|
HELP0(" SERVER_VERSION_NAME\n"
|
||||||
" SERVER_VERSION_NUM\n"
|
" SERVER_VERSION_NUM\n"
|
||||||
" server's version (in short string or numeric format)\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"
|
HELP0(" SHOW_ALL_RESULTS\n"
|
||||||
" show all results of a combined query (\\;) instead of only the last\n");
|
" show all results of a combined query (\\;) instead of only the last\n");
|
||||||
HELP0(" SHOW_CONTEXT\n"
|
HELP0(" SHOW_CONTEXT\n"
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
#include "postgres_fe.h"
|
#include "postgres_fe.h"
|
||||||
|
|
||||||
#include "psqlscanslash.h"
|
#include "psqlscanslash.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "fe_utils/conditional.h"
|
#include "fe_utils/conditional.h"
|
||||||
|
|
||||||
@ -772,6 +774,7 @@ evaluate_backtick(PsqlScanState state)
|
|||||||
PQExpBufferData cmd_output;
|
PQExpBufferData cmd_output;
|
||||||
FILE *fd;
|
FILE *fd;
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
int exit_code = 0;
|
||||||
char buf[512];
|
char buf[512];
|
||||||
size_t result;
|
size_t result;
|
||||||
|
|
||||||
@ -783,6 +786,7 @@ evaluate_backtick(PsqlScanState state)
|
|||||||
{
|
{
|
||||||
pg_log_error("%s: %m", cmd);
|
pg_log_error("%s: %m", cmd);
|
||||||
error = true;
|
error = true;
|
||||||
|
exit_code = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!error)
|
if (!error)
|
||||||
@ -800,10 +804,19 @@ evaluate_backtick(PsqlScanState state)
|
|||||||
} while (!feof(fd));
|
} 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))
|
if (PQExpBufferDataBroken(cmd_output))
|
||||||
@ -826,5 +839,10 @@ evaluate_backtick(PsqlScanState state)
|
|||||||
appendBinaryPQExpBuffer(output_buf, cmd_output.data, cmd_output.len);
|
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);
|
termPQExpBuffer(&cmd_output);
|
||||||
}
|
}
|
||||||
|
@ -127,3 +127,22 @@ wait_result_is_any_signal(int exit_status, bool include_command_not_found)
|
|||||||
return true;
|
return true;
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
@ -495,6 +495,7 @@ extern char *escape_single_quotes_ascii(const char *src);
|
|||||||
extern char *wait_result_to_str(int exitstatus);
|
extern char *wait_result_to_str(int exitstatus);
|
||||||
extern bool wait_result_is_signal(int exit_status, int signum);
|
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 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
|
* Interfaces that we assume all Unix system have. We retain individual macros
|
||||||
|
Reference in New Issue
Block a user