diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2016-04-04 15:25:16 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2016-04-04 15:25:16 -0400 |
commit | 2bbe9112aec60abc2d3b4c39e75d0cbdcaaa45e1 (patch) | |
tree | cd79ffca7612bdcd1ed77ff48b3f9e0a9be031b8 /src/bin/psql/common.c | |
parent | 66229ac0040cf1e0f5b9d72271aa9feaf3b3a37e (diff) | |
download | postgresql-2bbe9112aec60abc2d3b4c39e75d0cbdcaaa45e1.tar.gz postgresql-2bbe9112aec60abc2d3b4c39e75d0cbdcaaa45e1.zip |
Add a \gexec command to psql for evaluation of computed queries.
\gexec executes the just-entered query, like \g, but instead of printing
the results it takes each field as a SQL command to send to the server.
Computing a series of queries to be executed is a fairly common thing,
but up to now you always had to resort to kluges like writing the queries
to a file and then inputting the file. Now it can be done with no
intermediate step.
The implementation is fairly straightforward except for its interaction
with FETCH_COUNT. ExecQueryUsingCursor isn't capable of being called
recursively, and even if it were, its need to create a transaction
block interferes unpleasantly with the desired behavior of \gexec after
a failure of a generated query (i.e., that it can continue). Therefore,
disable use of ExecQueryUsingCursor when doing the master \gexec query.
We can still apply it to individual generated queries, however, and there
might be some value in doing so.
While testing this feature's interaction with single-step mode, I (tgl) was
led to conclude that SendQuery needs to recognize SIGINT (cancel_pressed)
as a negative response to the single-step prompt. Perhaps that's a
back-patchable bug fix, but for now I just included it here.
Corey Huinker, reviewed by Jim Nasby, Daniel Vérité, and myself
Diffstat (limited to 'src/bin/psql/common.c')
-rw-r--r-- | src/bin/psql/common.c | 90 |
1 files changed, 86 insertions, 4 deletions
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) { |