aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bin/psql/command.c4
-rw-r--r--src/bin/psql/common.c184
-rw-r--r--src/bin/psql/common.h1
-rw-r--r--src/bin/psql/copy.c10
-rw-r--r--src/bin/psql/print.c78
-rw-r--r--src/bin/psql/print.h4
-rw-r--r--src/bin/psql/startup.c3
7 files changed, 178 insertions, 106 deletions
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 8eca4cf23bb..6e8c62395c0 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -1531,6 +1531,7 @@ exec_command(const char *cmd,
if (fname[0] == '|')
{
is_pipe = true;
+ disable_sigpipe_trap();
fd = popen(&fname[1], "w");
}
else
@@ -1565,6 +1566,9 @@ exec_command(const char *cmd,
}
}
+ if (is_pipe)
+ restore_sigpipe_trap();
+
free(fname);
}
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 3254a140b3a..a287eeee195 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -26,72 +26,90 @@
#include "mbprint.h"
-
static bool ExecQueryUsingCursor(const char *query, double *elapsed_msec);
static bool command_no_begin(const char *query);
static bool is_select_command(const char *query);
+
/*
- * setQFout
- * -- handler for -o command line option and \o command
+ * openQueryOutputFile --- attempt to open a query output file
*
- * Tries to open file fname (or pipe if fname starts with '|')
- * and stores the file handle in pset)
- * Upon failure, sets stdout and returns false.
+ * fname == NULL selects stdout, else an initial '|' selects a pipe,
+ * else plain file.
+ *
+ * Returns output file pointer into *fout, and is-a-pipe flag into *is_pipe.
+ * Caller is responsible for adjusting SIGPIPE state if it's a pipe.
+ *
+ * On error, reports suitable error message and returns FALSE.
*/
bool
-setQFout(const char *fname)
+openQueryOutputFile(const char *fname, FILE **fout, bool *is_pipe)
{
- bool status = true;
-
- /* Close old file/pipe */
- if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr)
- {
- if (pset.queryFoutPipe)
- pclose(pset.queryFout);
- else
- fclose(pset.queryFout);
- }
-
- /* If no filename, set stdout */
if (!fname || fname[0] == '\0')
{
- pset.queryFout = stdout;
- pset.queryFoutPipe = false;
+ *fout = stdout;
+ *is_pipe = false;
}
else if (*fname == '|')
{
- pset.queryFout = popen(fname + 1, "w");
- pset.queryFoutPipe = true;
+ *fout = popen(fname + 1, "w");
+ *is_pipe = true;
}
else
{
- pset.queryFout = fopen(fname, "w");
- pset.queryFoutPipe = false;
+ *fout = fopen(fname, "w");
+ *is_pipe = false;
}
- if (!(pset.queryFout))
+ if (*fout == NULL)
{
psql_error("%s: %s\n", fname, strerror(errno));
- pset.queryFout = stdout;
- pset.queryFoutPipe = false;
- status = false;
+ return false;
}
- /* Direct signals */
-#ifndef WIN32
- pqsignal(SIGPIPE, pset.queryFoutPipe ? SIG_IGN : SIG_DFL);
-#endif
-
- return status;
+ return true;
}
+/*
+ * setQFout
+ * -- handler for -o command line option and \o command
+ *
+ * On success, updates pset with the new output file and returns true.
+ * On failure, returns false without changing pset state.
+ */
+bool
+setQFout(const char *fname)
+{
+ FILE *fout;
+ bool is_pipe;
+
+ /* First make sure we can open the new output file/pipe */
+ if (!openQueryOutputFile(fname, &fout, &is_pipe))
+ return false;
+
+ /* Close old file/pipe */
+ if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr)
+ {
+ if (pset.queryFoutPipe)
+ pclose(pset.queryFout);
+ else
+ fclose(pset.queryFout);
+ }
+
+ pset.queryFout = fout;
+ pset.queryFoutPipe = is_pipe;
+
+ /* Adjust SIGPIPE handling appropriately: ignore signal if is_pipe */
+ set_sigpipe_trap_state(is_pipe);
+ restore_sigpipe_trap();
+
+ return true;
+}
/*
* Error reporting for scripts. Errors should look like
* psql:filename:lineno: message
- *
*/
void
psql_error(const char *fmt,...)
@@ -611,27 +629,23 @@ PrintQueryTuples(const PGresult *results)
/* write output to \g argument, if any */
if (pset.gfname)
{
- /* keep this code in sync with ExecQueryUsingCursor */
- FILE *queryFout_copy = pset.queryFout;
- bool queryFoutPipe_copy = pset.queryFoutPipe;
-
- pset.queryFout = stdout; /* so it doesn't get closed */
+ FILE *fout;
+ bool is_pipe;
- /* open file/pipe */
- if (!setQFout(pset.gfname))
- {
- pset.queryFout = queryFout_copy;
- pset.queryFoutPipe = queryFoutPipe_copy;
+ if (!openQueryOutputFile(pset.gfname, &fout, &is_pipe))
return false;
- }
-
- printQuery(results, &my_popt, pset.queryFout, false, pset.logfile);
+ if (is_pipe)
+ disable_sigpipe_trap();
- /* close file/pipe, restore old setting */
- setQFout(NULL);
+ printQuery(results, &my_popt, fout, false, pset.logfile);
- pset.queryFout = queryFout_copy;
- pset.queryFoutPipe = queryFoutPipe_copy;
+ if (is_pipe)
+ {
+ pclose(fout);
+ restore_sigpipe_trap();
+ }
+ else
+ fclose(fout);
}
else
printQuery(results, &my_popt, pset.queryFout, false, pset.logfile);
@@ -1199,10 +1213,10 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
PGresult *results;
PQExpBufferData buf;
printQueryOpt my_popt = pset.popt;
- FILE *queryFout_copy = pset.queryFout;
- bool queryFoutPipe_copy = pset.queryFoutPipe;
+ FILE *fout;
+ bool is_pipe;
+ bool is_pager = false;
bool started_txn = false;
- bool did_pager = false;
int ntuples;
int fetch_count;
char fetch_cmd[64];
@@ -1268,21 +1282,22 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
/* prepare to write output to \g argument, if any */
if (pset.gfname)
{
- /* keep this code in sync with PrintQueryTuples */
- pset.queryFout = stdout; /* so it doesn't get closed */
-
- /* open file/pipe */
- if (!setQFout(pset.gfname))
+ if (!openQueryOutputFile(pset.gfname, &fout, &is_pipe))
{
- pset.queryFout = queryFout_copy;
- pset.queryFoutPipe = queryFoutPipe_copy;
OK = false;
goto cleanup;
}
+ if (is_pipe)
+ disable_sigpipe_trap();
+ }
+ else
+ {
+ fout = pset.queryFout;
+ is_pipe = false; /* doesn't matter */
}
/* clear any pre-existing error indication on the output stream */
- clearerr(pset.queryFout);
+ clearerr(fout);
for (;;)
{
@@ -1302,12 +1317,10 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
if (PQresultStatus(results) != PGRES_TUPLES_OK)
{
/* shut down pager before printing error message */
- if (did_pager)
+ if (is_pager)
{
- ClosePager(pset.queryFout);
- pset.queryFout = queryFout_copy;
- pset.queryFoutPipe = queryFoutPipe_copy;
- did_pager = false;
+ ClosePager(fout);
+ is_pager = false;
}
OK = AcceptResult(results);
@@ -1331,17 +1344,17 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
/* this is the last result set, so allow footer decoration */
my_popt.topt.stop_table = true;
}
- else if (pset.queryFout == stdout && !did_pager)
+ else if (fout == stdout && !is_pager)
{
/*
* If query requires multiple result sets, hack to ensure that
* only one pager instance is used for the whole mess
*/
- pset.queryFout = PageOutput(INT_MAX, &(my_popt.topt));
- did_pager = true;
+ fout = PageOutput(INT_MAX, &(my_popt.topt));
+ is_pager = true;
}
- printQuery(results, &my_popt, pset.queryFout, did_pager, pset.logfile);
+ printQuery(results, &my_popt, fout, is_pager, pset.logfile);
PQclear(results);
@@ -1355,7 +1368,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
* the pager dies/exits/etc, there's no sense throwing more data at
* it.
*/
- flush_error = fflush(pset.queryFout);
+ flush_error = fflush(fout);
/*
* Check if we are at the end, if a cancel was pressed, or if there
@@ -1365,24 +1378,25 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
* stop bothering to pull down more data.
*/
if (ntuples < fetch_count || cancel_pressed || flush_error ||
- ferror(pset.queryFout))
+ ferror(fout))
break;
}
- /* close \g argument file/pipe, restore old setting */
if (pset.gfname)
{
- /* keep this code in sync with PrintQueryTuples */
- setQFout(NULL);
-
- pset.queryFout = queryFout_copy;
- pset.queryFoutPipe = queryFoutPipe_copy;
+ /* close \g argument file/pipe */
+ if (is_pipe)
+ {
+ pclose(fout);
+ restore_sigpipe_trap();
+ }
+ else
+ fclose(fout);
}
- else if (did_pager)
+ else if (is_pager)
{
- ClosePager(pset.queryFout);
- pset.queryFout = queryFout_copy;
- pset.queryFoutPipe = queryFoutPipe_copy;
+ /* close transient pager */
+ ClosePager(fout);
}
cleanup:
diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h
index caf31d19b89..62a602632a1 100644
--- a/src/bin/psql/common.h
+++ b/src/bin/psql/common.h
@@ -16,6 +16,7 @@
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
+extern bool openQueryOutputFile(const char *fname, FILE **fout, bool *is_pipe);
extern bool setQFout(const char *fname);
extern void psql_error(const char *fmt,...) pg_attribute_printf(1, 2);
diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c
index c0fc28373a9..a09408bf550 100644
--- a/src/bin/psql/copy.c
+++ b/src/bin/psql/copy.c
@@ -37,7 +37,7 @@
* where 'filename' can be one of the following:
* '<file path>' | PROGRAM '<command>' | stdin | stdout | pstdout | pstdout
* and 'query' can be one of the following:
- * SELECT | UPDATE | INSERT | DELETE
+ * SELECT | UPDATE | INSERT | DELETE
*
* An undocumented fact is that you can still write BINARY before the
* tablename; this is a hangover from the pre-7.3 syntax. The options
@@ -312,9 +312,7 @@ do_copy(const char *args)
fflush(stdout);
fflush(stderr);
errno = 0;
-#ifndef WIN32
- pqsignal(SIGPIPE, SIG_IGN);
-#endif
+ disable_sigpipe_trap();
copystream = popen(options->file, PG_BINARY_W);
}
else
@@ -399,9 +397,7 @@ do_copy(const char *args)
}
success = false;
}
-#ifndef WIN32
- pqsignal(SIGPIPE, SIG_DFL);
-#endif
+ restore_sigpipe_trap();
}
else
{
diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c
index 190f2bc5d85..05d4b3162c3 100644
--- a/src/bin/psql/print.c
+++ b/src/bin/psql/print.c
@@ -39,6 +39,13 @@
*/
volatile bool cancel_pressed = false;
+/*
+ * Likewise, the sigpipe_trap and pager open/close functions are here rather
+ * than in common.c so that this file can be used by non-psql programs.
+ */
+static bool always_ignore_sigpipe = false;
+
+
/* info for locale-aware numeric formatting; set up by setDecimalLocale() */
static char *decimal_point;
static int groupdigits;
@@ -2775,11 +2782,62 @@ print_troff_ms_vertical(const printTableContent *cont, FILE *fout)
/********************************/
-/* Public functions */
+/* Public functions */
/********************************/
/*
+ * disable_sigpipe_trap
+ *
+ * Turn off SIGPIPE interrupt --- call this before writing to a temporary
+ * query output file that is a pipe.
+ *
+ * No-op on Windows, where there's no SIGPIPE interrupts.
+ */
+void
+disable_sigpipe_trap(void)
+{
+#ifndef WIN32
+ pqsignal(SIGPIPE, SIG_IGN);
+#endif
+}
+
+/*
+ * restore_sigpipe_trap
+ *
+ * Restore normal SIGPIPE interrupt --- call this when done writing to a
+ * temporary query output file that was (or might have been) a pipe.
+ *
+ * Note: within psql, we enable SIGPIPE interrupts unless the permanent query
+ * output file is a pipe, in which case they should be kept off. This
+ * approach works only because psql is not currently complicated enough to
+ * have nested usages of short-lived output files. Otherwise we'd probably
+ * need a genuine save-and-restore-state approach; but for now, that would be
+ * useless complication. In non-psql programs, this always enables SIGPIPE.
+ *
+ * No-op on Windows, where there's no SIGPIPE interrupts.
+ */
+void
+restore_sigpipe_trap(void)
+{
+#ifndef WIN32
+ pqsignal(SIGPIPE, always_ignore_sigpipe ? SIG_IGN : SIG_DFL);
+#endif
+}
+
+/*
+ * set_sigpipe_trap_state
+ *
+ * Set the trap state that restore_sigpipe_trap should restore to.
+ */
+void
+set_sigpipe_trap_state(bool ignore)
+{
+ always_ignore_sigpipe = ignore;
+}
+
+
+/*
* PageOutput
*
* Tests if pager is needed and returns appropriate FILE pointer.
@@ -2792,9 +2850,6 @@ PageOutput(int lines, const printTableOpt *topt)
/* check whether we need / can / are supposed to use pager */
if (topt && topt->pager && isatty(fileno(stdin)) && isatty(fileno(stdout)))
{
- const char *pagerprog;
- FILE *pagerpipe;
-
#ifdef TIOCGWINSZ
unsigned short int pager = topt->pager;
int min_lines = topt->pager_min_lines;
@@ -2807,20 +2862,19 @@ PageOutput(int lines, const printTableOpt *topt)
if (result == -1
|| (lines >= screen_size.ws_row && lines >= min_lines)
|| pager > 1)
- {
#endif
+ {
+ const char *pagerprog;
+ FILE *pagerpipe;
+
pagerprog = getenv("PAGER");
if (!pagerprog)
pagerprog = DEFAULT_PAGER;
-#ifndef WIN32
- pqsignal(SIGPIPE, SIG_IGN);
-#endif
+ disable_sigpipe_trap();
pagerpipe = popen(pagerprog, "w");
if (pagerpipe)
return pagerpipe;
-#ifdef TIOCGWINSZ
}
-#endif
}
return stdout;
@@ -2848,9 +2902,7 @@ ClosePager(FILE *pagerpipe)
fprintf(pagerpipe, _("Interrupted\n"));
pclose(pagerpipe);
-#ifndef WIN32
- pqsignal(SIGPIPE, SIG_DFL);
-#endif
+ restore_sigpipe_trap();
}
}
diff --git a/src/bin/psql/print.h b/src/bin/psql/print.h
index df514cffb0c..fd56598426d 100644
--- a/src/bin/psql/print.h
+++ b/src/bin/psql/print.h
@@ -167,6 +167,10 @@ extern const printTextFormat pg_asciiformat_old;
extern const printTextFormat pg_utf8format;
+extern void disable_sigpipe_trap(void);
+extern void restore_sigpipe_trap(void);
+extern void set_sigpipe_trap_state(bool ignore);
+
extern FILE *PageOutput(int lines, const printTableOpt *topt);
extern void ClosePager(FILE *pagerpipe);
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 7aa997d479c..4e5021a43df 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -461,7 +461,8 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
options->no_readline = true;
break;
case 'o':
- setQFout(optarg);
+ if (!setQFout(optarg))
+ exit(EXIT_FAILURE);
break;
case 'p':
options->port = pg_strdup(optarg);