Commit 101d6ae7 authored by Tom Lane's avatar Tom Lane

Prevent "\g filename" from affecting subsequent commands after an error.

In the previous coding, psql's state variable saying that output should
go to a file was only reset after successful completion of a query
returning tuples.  Thus for example,

regression=# select 1/0
regression-# \g somefile
ERROR:  division by zero
regression=# select 1/2;
regression=#

... huh, I wonder where that output went.  Even more oddly, the state
was not reset even if it's the file that's causing the failure:

regression=# select 1/2 \g /foo
/foo: Permission denied
regression=# select 1/2;
/foo: Permission denied
regression=# select 1/2;
/foo: Permission denied

This seems to me not to satisfy the principle of least surprise.
\g is certainly not documented in a way that suggests its effects are
at all persistent.

To fix, adjust the code so that the flag is reset at exit from SendQuery
no matter what happened.

Noted while reviewing the \gset patch, which had comparable issues.
Arguably this is a bug fix, but I'll refrain from back-patching for now.
parent 84725aa5
...@@ -1608,9 +1608,13 @@ Tue Oct 26 21:40:57 CEST 1999 ...@@ -1608,9 +1608,13 @@ Tue Oct 26 21:40:57 CEST 1999
optionally stores the query's output in <replaceable optionally stores the query's output in <replaceable
class="parameter">filename</replaceable> or pipes the output class="parameter">filename</replaceable> or pipes the output
into a separate Unix shell executing <replaceable into a separate Unix shell executing <replaceable
class="parameter">command</replaceable>. A bare class="parameter">command</replaceable>. The file or command is
<literal>\g</literal> is virtually equivalent to a semicolon. A written to only if the query successfully returns zero or more tuples,
<literal>\g</literal> with argument is a <quote>one-shot</quote> not if the query fails or is a non-data-returning SQL command.
</para>
<para>
A bare <literal>\g</literal> is essentially equivalent to a semicolon.
A <literal>\g</literal> with argument is a <quote>one-shot</quote>
alternative to the <command>\o</command> command. alternative to the <command>\o</command> command.
</para> </para>
</listitem> </listitem>
......
...@@ -731,7 +731,7 @@ exec_command(const char *cmd, ...@@ -731,7 +731,7 @@ exec_command(const char *cmd,
free(fname); free(fname);
} }
/* \g means send query */ /* \g [filename] means send query, optionally with output to file/pipe */
else if (strcmp(cmd, "g") == 0) else if (strcmp(cmd, "g") == 0)
{ {
char *fname = psql_scan_slash_option(scan_state, char *fname = psql_scan_slash_option(scan_state,
......
...@@ -607,9 +607,6 @@ PrintQueryTuples(const PGresult *results) ...@@ -607,9 +607,6 @@ PrintQueryTuples(const PGresult *results)
pset.queryFout = queryFout_copy; pset.queryFout = queryFout_copy;
pset.queryFoutPipe = queryFoutPipe_copy; pset.queryFoutPipe = queryFoutPipe_copy;
free(pset.gfname);
pset.gfname = NULL;
} }
else else
printQuery(results, &my_popt, pset.queryFout, pset.logfile); printQuery(results, &my_popt, pset.queryFout, pset.logfile);
...@@ -835,14 +832,14 @@ SendQuery(const char *query) ...@@ -835,14 +832,14 @@ SendQuery(const char *query)
PGresult *results; PGresult *results;
PGTransactionStatusType transaction_status; PGTransactionStatusType transaction_status;
double elapsed_msec = 0; double elapsed_msec = 0;
bool OK, bool OK = false;
on_error_rollback_savepoint = false; bool on_error_rollback_savepoint = false;
static bool on_error_rollback_warning = false; static bool on_error_rollback_warning = false;
if (!pset.db) if (!pset.db)
{ {
psql_error("You are currently not connected to a database.\n"); psql_error("You are currently not connected to a database.\n");
return false; goto sendquery_cleanup;
} }
if (pset.singlestep) if (pset.singlestep)
...@@ -856,7 +853,7 @@ SendQuery(const char *query) ...@@ -856,7 +853,7 @@ SendQuery(const char *query)
fflush(stdout); fflush(stdout);
if (fgets(buf, sizeof(buf), stdin) != NULL) if (fgets(buf, sizeof(buf), stdin) != NULL)
if (buf[0] == 'x') if (buf[0] == 'x')
return false; goto sendquery_cleanup;
} }
else if (pset.echo == PSQL_ECHO_QUERIES) else if (pset.echo == PSQL_ECHO_QUERIES)
{ {
...@@ -887,7 +884,7 @@ SendQuery(const char *query) ...@@ -887,7 +884,7 @@ SendQuery(const char *query)
psql_error("%s", PQerrorMessage(pset.db)); psql_error("%s", PQerrorMessage(pset.db));
PQclear(results); PQclear(results);
ResetCancelConn(); ResetCancelConn();
return false; goto sendquery_cleanup;
} }
PQclear(results); PQclear(results);
transaction_status = PQtransactionStatus(pset.db); transaction_status = PQtransactionStatus(pset.db);
...@@ -912,7 +909,7 @@ SendQuery(const char *query) ...@@ -912,7 +909,7 @@ SendQuery(const char *query)
psql_error("%s", PQerrorMessage(pset.db)); psql_error("%s", PQerrorMessage(pset.db));
PQclear(results); PQclear(results);
ResetCancelConn(); ResetCancelConn();
return false; goto sendquery_cleanup;
} }
PQclear(results); PQclear(results);
on_error_rollback_savepoint = true; on_error_rollback_savepoint = true;
...@@ -1008,10 +1005,11 @@ SendQuery(const char *query) ...@@ -1008,10 +1005,11 @@ SendQuery(const char *query)
{ {
psql_error("%s", PQerrorMessage(pset.db)); psql_error("%s", PQerrorMessage(pset.db));
PQclear(svptres); PQclear(svptres);
OK = false;
PQclear(results); PQclear(results);
ResetCancelConn(); ResetCancelConn();
return false; goto sendquery_cleanup;
} }
PQclear(svptres); PQclear(svptres);
} }
...@@ -1037,6 +1035,17 @@ SendQuery(const char *query) ...@@ -1037,6 +1035,17 @@ SendQuery(const char *query)
PrintNotifications(); PrintNotifications();
/* perform cleanup that should occur after any attempted query */
sendquery_cleanup:
/* reset \g's output-to-filename trigger */
if (pset.gfname)
{
free(pset.gfname);
pset.gfname = NULL;
}
return OK; return OK;
} }
...@@ -1218,9 +1227,6 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec) ...@@ -1218,9 +1227,6 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
pset.queryFout = queryFout_copy; pset.queryFout = queryFout_copy;
pset.queryFoutPipe = queryFoutPipe_copy; pset.queryFoutPipe = queryFoutPipe_copy;
free(pset.gfname);
pset.gfname = NULL;
} }
else if (did_pager) else if (did_pager)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment