#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,...)
/* 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);
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];
/* 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 (;;)
{
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);
/* 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);
* 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
* 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:
*/
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;
/********************************/
-/* 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
*
/* 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;
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;
fprintf(pagerpipe, _("Interrupted\n"));
pclose(pagerpipe);
-#ifndef WIN32
- pqsignal(SIGPIPE, SIG_DFL);
-#endif
+ restore_sigpipe_trap();
}
}