</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>\gexec</literal></term>
+
+ <listitem>
+ <para>
+ 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 <structname>my_table</>:
+<programlisting>
+=> <userinput>SELECT format('create index on my_table(%I)', attname)</>
+-> <userinput>FROM pg_attribute</>
+-> <userinput>WHERE attrelid = 'my_table'::regclass AND attnum > 0</>
+-> <userinput>ORDER BY attnum</>
+-> <userinput>\gexec</>
+CREATE INDEX
+CREATE INDEX
+CREATE INDEX
+CREATE INDEX
+</programlisting>
+ </para>
+
+ <para>
+ 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
+ <application>psql</> meta-commands nor contain <application>psql</>
+ variable references. If any individual query fails, execution of
+ the remaining queries continues
+ unless <varname>ON_ERROR_STOP</varname> is set. Execution of each
+ query is subject to <varname>ECHO</varname> processing.
+ (Setting <varname>ECHO</varname> to <literal>all</literal>
+ or <literal>queries</literal> is often advisable when
+ using <command>\gexec</>.) Query logging, single-step mode,
+ timing, and other query execution features apply to each generated
+ query as well.
+ </para>
+ </listitem>
+ </varlistentry>
+
+
<varlistentry>
<term><literal>\gset [ <replaceable class="parameter">prefix</replaceable> ]</literal></term>
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)
{
}
+/*
+ * 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
*
/*
- * 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.
*
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 */
{
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"),
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)
{
}
}
- 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,
pset.gset_prefix = NULL;
}
+ /* reset \gexec trigger */
+ pset.gexec_flag = false;
+
return OK;
}
break;
}
+ /* Note we do not deal with \gexec mode here */
+
ntuples = PQntuples(results);
if (ntuples < fetch_count)
{
OK = AcceptResult(results) &&
(PQresultStatus(results) == PGRES_COMMAND_OK);
+ ClearOrSaveResult(results);
}
- ClearOrSaveResult(results);
+ else
+ PQclear(results);
if (started_txn)
{
* 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"));
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) */
"\\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",
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
\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