diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index e8afc247af..d8b9a03ee0 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1766,6 +1766,49 @@ Tue Oct 26 21:40:57 CEST 1999
+
+
+ \gexec
+
+
+
+ Sends the current query input buffer to the server, then treats
+ each column of each row of the query's output (if any) as a SQL
+ statement to be executed. For example, to create an index on each
+ column of my_table>:
+
+=> SELECT format('create index on my_table(%I)', attname)>
+-> FROM pg_attribute>
+-> WHERE attrelid = 'my_table'::regclass AND attnum > 0>
+-> ORDER BY attnum>
+-> \gexec>
+CREATE INDEX
+CREATE INDEX
+CREATE INDEX
+CREATE INDEX
+
+
+
+
+ The generated queries are executed in the order in which the rows
+ are returned, and left-to-right within each row if there is more
+ than one column. NULL fields are ignored. The generated queries
+ are sent literally to the server for processing, so they cannot be
+ psql> meta-commands nor contain psql>
+ variable references. If any individual query fails, execution of
+ the remaining queries continues
+ unless ON_ERROR_STOP is set. Execution of each
+ query is subject to ECHO processing.
+ (Setting ECHO to all
+ or queries is often advisable when
+ using \gexec>.) Query logging, single-step mode,
+ timing, and other query execution features apply to each generated
+ query as well.
+
+
+
+
+
\gset [ prefix ]
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 3401b5183b..1d326a81af 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 a2a07fb538..df3441cc75 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -797,6 +797,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
*
@@ -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 c6f0993018..7549451d21 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 ae30b2e60e..c69f6ba1ec 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 688d92a452..cb8a06d15e 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",
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 178a8093b7..edcc414630 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -51,6 +51,51 @@ select 10 as test01, 20 as test02 from generate_series(1,3) \gset
more than one row returned for \gset
select 10 as test01, 20 as test02 from generate_series(1,0) \gset
no rows returned for \gset
+\unset FETCH_COUNT
+-- \gexec
+create temporary table gexec_test(a int, b text, c date, d float);
+select format('create index on gexec_test(%I)', attname)
+from pg_attribute
+where attrelid = 'gexec_test'::regclass and attnum > 0
+order by attnum
+\gexec
+create index on gexec_test(a)
+create index on gexec_test(b)
+create index on gexec_test(c)
+create index on gexec_test(d)
+-- \gexec should work in FETCH_COUNT mode too
+-- (though the fetch limit applies to the executed queries not the meta query)
+\set FETCH_COUNT 1
+select 'select 1 as ones', 'select x.y, x.y*2 as double from generate_series(1,4) as x(y)'
+union all
+select 'drop table gexec_test', NULL
+union all
+select 'drop table gexec_test', 'select ''2000-01-01''::date as party_over'
+\gexec
+select 1 as ones
+ ones
+------
+ 1
+(1 row)
+
+select x.y, x.y*2 as double from generate_series(1,4) as x(y)
+ y | double
+---+--------
+ 1 | 2
+ 2 | 4
+ 3 | 6
+ 4 | 8
+(4 rows)
+
+drop table gexec_test
+drop table gexec_test
+ERROR: table "gexec_test" does not exist
+select '2000-01-01'::date as party_over
+ party_over
+------------
+ 01-01-2000
+(1 row)
+
\unset FETCH_COUNT
-- show all pset options
\pset
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 2f81380e22..c5b3664949 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -38,6 +38,28 @@ select 10 as test01, 20 as test02 from generate_series(1,0) \gset
\unset FETCH_COUNT
+-- \gexec
+
+create temporary table gexec_test(a int, b text, c date, d float);
+select format('create index on gexec_test(%I)', attname)
+from pg_attribute
+where attrelid = 'gexec_test'::regclass and attnum > 0
+order by attnum
+\gexec
+
+-- \gexec should work in FETCH_COUNT mode too
+-- (though the fetch limit applies to the executed queries not the meta query)
+\set FETCH_COUNT 1
+
+select 'select 1 as ones', 'select x.y, x.y*2 as double from generate_series(1,4) as x(y)'
+union all
+select 'drop table gexec_test', NULL
+union all
+select 'drop table gexec_test', 'select ''2000-01-01''::date as party_over'
+\gexec
+
+\unset FETCH_COUNT
+
-- show all pset options
\pset