aboutsummaryrefslogtreecommitdiff
path: root/src/bin
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin')
-rw-r--r--src/bin/psql/command.c7
-rw-r--r--src/bin/psql/common.c90
-rw-r--r--src/bin/psql/help.c3
-rw-r--r--src/bin/psql/settings.h1
-rw-r--r--src/bin/psql/tab-complete.c2
5 files changed, 97 insertions, 6 deletions
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 3401b5183b0..1d326a81afe 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -871,6 +871,13 @@ exec_command(const char *cmd,
status = PSQL_CMD_SEND;
}
+ /* \gexec -- send query and execute each field of result */
+ else if (strcmp(cmd, "gexec") == 0)
+ {
+ pset.gexec_flag = true;
+ status = PSQL_CMD_SEND;
+ }
+
/* \gset [prefix] -- send query and store result into variables */
else if (strcmp(cmd, "gset") == 0)
{
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index a2a07fb538e..df3441cc750 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -798,6 +798,76 @@ StoreQueryTuple(const PGresult *result)
/*
+ * ExecQueryTuples: assuming query result is OK, execute each query
+ * result field as a SQL statement
+ *
+ * Returns true if successful, false otherwise.
+ */
+static bool
+ExecQueryTuples(const PGresult *result)
+{
+ bool success = true;
+ int nrows = PQntuples(result);
+ int ncolumns = PQnfields(result);
+ int r,
+ c;
+
+ /*
+ * We must turn off gexec_flag to avoid infinite recursion. Note that
+ * this allows ExecQueryUsingCursor to be applied to the individual query
+ * results. SendQuery prevents it from being applied when fetching the
+ * queries-to-execute, because it can't handle recursion either.
+ */
+ pset.gexec_flag = false;
+
+ for (r = 0; r < nrows; r++)
+ {
+ for (c = 0; c < ncolumns; c++)
+ {
+ if (!PQgetisnull(result, r, c))
+ {
+ const char *query = PQgetvalue(result, r, c);
+
+ /* Abandon execution if cancel_pressed */
+ if (cancel_pressed)
+ goto loop_exit;
+
+ /*
+ * ECHO_ALL mode should echo these queries, but SendQuery
+ * assumes that MainLoop did that, so we have to do it here.
+ */
+ if (pset.echo == PSQL_ECHO_ALL && !pset.singlestep)
+ {
+ puts(query);
+ fflush(stdout);
+ }
+
+ if (!SendQuery(query))
+ {
+ /* Error - abandon execution if ON_ERROR_STOP */
+ success = false;
+ if (pset.on_error_stop)
+ goto loop_exit;
+ }
+ }
+ }
+ }
+
+loop_exit:
+
+ /*
+ * Restore state. We know gexec_flag was on, else we'd not be here. (We
+ * also know it'll get turned off at end of command, but that's not ours
+ * to do here.)
+ */
+ pset.gexec_flag = true;
+
+ /* Return true if all queries were successful */
+ return success;
+}
+
+
+/*
* ProcessResult: utility function for use by SendQuery() only
*
* When our command string contained a COPY FROM STDIN or COPY TO STDOUT,
@@ -971,7 +1041,7 @@ PrintQueryStatus(PGresult *results)
/*
- * PrintQueryResults: print out (or store) query results as required
+ * PrintQueryResults: print out (or store or execute) query results as required
*
* Note: Utility function for use by SendQuery() only.
*
@@ -989,9 +1059,11 @@ PrintQueryResults(PGresult *results)
switch (PQresultStatus(results))
{
case PGRES_TUPLES_OK:
- /* store or print the data ... */
+ /* store or execute or print the data ... */
if (pset.gset_prefix)
success = StoreQueryTuple(results);
+ else if (pset.gexec_flag)
+ success = ExecQueryTuples(results);
else
success = PrintQueryTuples(results);
/* if it's INSERT/UPDATE/DELETE RETURNING, also print status */
@@ -1068,6 +1140,7 @@ SendQuery(const char *query)
{
char buf[3];
+ fflush(stderr);
printf(_("***(Single step mode: verify command)*******************************************\n"
"%s\n"
"***(press return to proceed or enter x and return to cancel)********************\n"),
@@ -1076,6 +1149,8 @@ SendQuery(const char *query)
if (fgets(buf, sizeof(buf), stdin) != NULL)
if (buf[0] == 'x')
goto sendquery_cleanup;
+ if (cancel_pressed)
+ goto sendquery_cleanup;
}
else if (pset.echo == PSQL_ECHO_QUERIES)
{
@@ -1138,7 +1213,7 @@ SendQuery(const char *query)
}
}
- if (pset.fetch_count <= 0 || !is_select_command(query))
+ if (pset.fetch_count <= 0 || pset.gexec_flag || !is_select_command(query))
{
/* Default fetch-it-all-and-print mode */
instr_time before,
@@ -1278,6 +1353,9 @@ sendquery_cleanup:
pset.gset_prefix = NULL;
}
+ /* reset \gexec trigger */
+ pset.gexec_flag = false;
+
return OK;
}
@@ -1423,6 +1501,8 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
break;
}
+ /* Note we do not deal with \gexec mode here */
+
ntuples = PQntuples(results);
if (ntuples < fetch_count)
@@ -1499,8 +1579,10 @@ cleanup:
{
OK = AcceptResult(results) &&
(PQresultStatus(results) == PGRES_COMMAND_OK);
+ ClearOrSaveResult(results);
}
- ClearOrSaveResult(results);
+ else
+ PQclear(results);
if (started_txn)
{
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index c6f0993018f..7549451d216 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -168,12 +168,13 @@ slashUsage(unsigned short int pager)
* Use "psql --help=commands | wc" to count correctly. It's okay to count
* the USE_READLINE line even in builds without that.
*/
- output = PageOutput(110, pager ? &(pset.popt.topt) : NULL);
+ output = PageOutput(111, pager ? &(pset.popt.topt) : NULL);
fprintf(output, _("General\n"));
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
fprintf(output, _(" \\errverbose show most recent error message at maximum verbosity\n"));
fprintf(output, _(" \\g [FILE] or ; execute query (and send results to file or |pipe)\n"));
+ fprintf(output, _(" \\gexec execute query, then execute each value in its result\n"));
fprintf(output, _(" \\gset [PREFIX] execute query and store results in psql variables\n"));
fprintf(output, _(" \\q quit psql\n"));
fprintf(output, _(" \\watch [SEC] execute query every SEC seconds\n"));
diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h
index ae30b2e60e3..c69f6ba1ec4 100644
--- a/src/bin/psql/settings.h
+++ b/src/bin/psql/settings.h
@@ -92,6 +92,7 @@ typedef struct _psqlSettings
char *gfname; /* one-shot file output argument for \g */
char *gset_prefix; /* one-shot prefix argument for \gset */
+ bool gexec_flag; /* one-shot flag to execute query's results */
bool notty; /* stdin or stdout is not a tty (as determined
* on startup) */
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 688d92a4520..cb8a06d15e4 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1281,7 +1281,7 @@ psql_completion(const char *text, int start, int end)
"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\drds", "\\ds", "\\dS",
"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
"\\e", "\\echo", "\\ef", "\\encoding", "\\errverbose", "\\ev",
- "\\f", "\\g", "\\gset", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l",
+ "\\f", "\\g", "\\gexec", "\\gset", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l",
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
"\\s", "\\set", "\\setenv", "\\sf", "\\sv", "\\t", "\\T",