Over in https://www.postgresql.org/message-id/eaf326ad693e74eba068f33a7f518...@oss.nttdata.com Justin Pryzby suggested that psql might need the ability to capture the shell exit code.
This is a POC patch that does that, but doesn't touch on the ON_ERROR_STOP stuff. I've added some very rudimentary tests, but haven't touched the documentation, because I strongly suspect that someone will suggest a better name for the variable. But basically, it works like this -- SHELL_EXIT_CODE is undefined \echo :SHELL_EXIT_CODE :SHELL_EXIT_CODE -- bad \! \! borp sh: line 1: borp: command not found \echo :SHELL_EXIT_CODE 32512 -- bad backtick \set var `borp` sh: line 1: borp: command not found \echo :SHELL_EXIT_CODE 127 -- good \! \! true \echo :SHELL_EXIT_CODE 0 -- play with exit codes \! exit 4 \echo :SHELL_EXIT_CODE 1024 \set var `exit 3` \echo :SHELL_EXIT_CODE 3 Feedback welcome.
From 119837575f4d0da804d92ec797bbf11e8075e595 Mon Sep 17 00:00:00 2001 From: coreyhuinker <corey.huin...@gmail.com> Date: Fri, 4 Nov 2022 04:45:39 -0400 Subject: [PATCH] POC: expose shell exit code as a psql variable --- src/bin/psql/command.c | 4 ++++ src/bin/psql/help.c | 2 ++ src/bin/psql/psqlscanslash.l | 28 +++++++++++++++++++++++++--- src/test/regress/expected/psql.out | 11 +++++++++++ src/test/regress/sql/psql.sql | 7 +++++++ 5 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index ab613dd49e..4666a63051 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -4957,6 +4957,7 @@ static bool do_shell(const char *command) { int result; + char exit_code_buf[32]; fflush(NULL); if (!command) @@ -4984,6 +4985,9 @@ do_shell(const char *command) else result = system(command); + snprintf(exit_code_buf, sizeof(exit_code_buf), "%d", WEXITSTATUS(result)); + SetVariable(pset.vars, "SHELL_EXIT_CODE", exit_code_buf); + if (result == 127 || result == -1) { pg_log_error("\\!: failed"); diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index f8ce1a0706..04f996332e 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -454,6 +454,8 @@ helpVariables(unsigned short int pager) " show all results of a combined query (\\;) instead of only the last\n"); HELP0(" SHOW_CONTEXT\n" " controls display of message context fields [never, errors, always]\n"); + HELP0(" SHELL_EXIT_CODE\n" + " Exit code of the last shell command\n"); HELP0(" SINGLELINE\n" " if set, end of line terminates SQL commands (same as -S option)\n"); HELP0(" SINGLESTEP\n" diff --git a/src/bin/psql/psqlscanslash.l b/src/bin/psql/psqlscanslash.l index a467b72144..30e6f5dcd4 100644 --- a/src/bin/psql/psqlscanslash.l +++ b/src/bin/psql/psqlscanslash.l @@ -27,6 +27,8 @@ %{ #include "fe_utils/psqlscan_int.h" +#include "settings.h" +#include "variables.h" /* * We must have a typedef YYSTYPE for yylex's first argument, but this lexer @@ -774,6 +776,8 @@ evaluate_backtick(PsqlScanState state) bool error = false; char buf[512]; size_t result; + int exit_code = 0; + char exit_code_buf[32]; initPQExpBuffer(&cmd_output); @@ -783,6 +787,7 @@ evaluate_backtick(PsqlScanState state) { pg_log_error("%s: %m", cmd); error = true; + exit_code = -1; } if (!error) @@ -800,10 +805,25 @@ evaluate_backtick(PsqlScanState state) } while (!feof(fd)); } - if (fd && pclose(fd) == -1) + if (fd) { - pg_log_error("%s: %m", cmd); - error = true; + exit_code = pclose(fd); + if (exit_code == -1) + { + pg_log_error("%s: %m", cmd); + error = true; + } + if (WIFEXITED(exit_code)) + { + exit_code=WEXITSTATUS(exit_code); + } + else if(WIFSIGNALED(exit_code)) { + exit_code=WTERMSIG(exit_code); + } + else if(WIFSTOPPED(exit_code)) { + //If you need to act upon the process stopping, do it here. + exit_code=WSTOPSIG(exit_code); + } } if (PQExpBufferDataBroken(cmd_output)) @@ -826,5 +846,7 @@ evaluate_backtick(PsqlScanState state) appendBinaryPQExpBuffer(output_buf, cmd_output.data, cmd_output.len); } + snprintf(exit_code_buf, sizeof(exit_code_buf), "%d", exit_code); + SetVariable(pset.vars, "SHELL_EXIT_CODE", exit_code_buf); termPQExpBuffer(&cmd_output); } diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index a7f5700edc..33202ffda7 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -1275,6 +1275,17 @@ execute q; +----+-------------+ deallocate q; +-- test SHELL_EXIT_CODE +\! nosuchcommand +sh: line 1: nosuchcommand: command not found +\echo :SHELL_EXIT_CODE +127 +\set nosuchvar `nosuchcommand` +sh: line 1: nosuchcommand: command not found +\! nosuchcommand +sh: line 1: nosuchcommand: command not found +\echo :SHELL_EXIT_CODE +127 -- test single-line header and data prepare q as select repeat('x',2*n) as "0123456789abcdef", repeat('y',20-2*n) as "0123456789" from generate_series(1,10) as n; \pset linestyle ascii diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql index 1149c6a839..99b874d5a9 100644 --- a/src/test/regress/sql/psql.sql +++ b/src/test/regress/sql/psql.sql @@ -277,6 +277,13 @@ execute q; deallocate q; +-- test SHELL_EXIT_CODE +\! nosuchcommand +\echo :SHELL_EXIT_CODE +\set nosuchvar `nosuchcommand` +\! nosuchcommand +\echo :SHELL_EXIT_CODE + -- test single-line header and data prepare q as select repeat('x',2*n) as "0123456789abcdef", repeat('y',20-2*n) as "0123456789" from generate_series(1,10) as n; -- 2.38.1