aboutsummaryrefslogtreecommitdiff
path: root/src/bin/psql/common.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/psql/common.c')
-rw-r--r--src/bin/psql/common.c71
1 files changed, 71 insertions, 0 deletions
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index b99705886fa..9b59ee840b9 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -549,10 +549,57 @@ AcceptResult(const PGresult *result)
/*
+ * Set special variables from a query result
+ * - ERROR: true/false, whether an error occurred on this query
+ * - SQLSTATE: code of error, or "00000" if no error, or "" if unknown
+ * - ROW_COUNT: how many rows were returned or affected, or "0"
+ * - LAST_ERROR_SQLSTATE: same for last error
+ * - LAST_ERROR_MESSAGE: message of last error
+ *
+ * Note: current policy is to apply this only to the results of queries
+ * entered by the user, not queries generated by slash commands.
+ */
+static void
+SetResultVariables(PGresult *results, bool success)
+{
+ if (success)
+ {
+ const char *ntuples = PQcmdTuples(results);
+
+ SetVariable(pset.vars, "ERROR", "false");
+ SetVariable(pset.vars, "SQLSTATE", "00000");
+ SetVariable(pset.vars, "ROW_COUNT", *ntuples ? ntuples : "0");
+ }
+ else
+ {
+ const char *code = PQresultErrorField(results, PG_DIAG_SQLSTATE);
+ const char *mesg = PQresultErrorField(results, PG_DIAG_MESSAGE_PRIMARY);
+
+ SetVariable(pset.vars, "ERROR", "true");
+
+ /*
+ * If there is no SQLSTATE code, use an empty string. This can happen
+ * for libpq-detected errors (e.g., lost connection, ENOMEM).
+ */
+ if (code == NULL)
+ code = "";
+ SetVariable(pset.vars, "SQLSTATE", code);
+ SetVariable(pset.vars, "ROW_COUNT", "0");
+ SetVariable(pset.vars, "LAST_ERROR_SQLSTATE", code);
+ SetVariable(pset.vars, "LAST_ERROR_MESSAGE", mesg ? mesg : "");
+ }
+}
+
+
+/*
* ClearOrSaveResult
*
* If the result represents an error, remember it for possible display by
* \errverbose. Otherwise, just PQclear() it.
+ *
+ * Note: current policy is to apply this to the results of all queries,
+ * including "back door" queries, for debugging's sake. It's OK to use
+ * PQclear() directly on results known to not be error results, however.
*/
static void
ClearOrSaveResult(PGresult *result)
@@ -1107,6 +1154,8 @@ ProcessResult(PGresult **results)
first_cycle = false;
}
+ SetResultVariables(*results, success);
+
/* may need this to recover from conn loss during COPY */
if (!first_cycle && !CheckConnection())
return false;
@@ -1526,6 +1575,7 @@ DescribeQuery(const char *query, double *elapsed_msec)
if (PQresultStatus(results) != PGRES_COMMAND_OK)
{
psql_error("%s", PQerrorMessage(pset.db));
+ SetResultVariables(results, false);
ClearOrSaveResult(results);
return false;
}
@@ -1599,6 +1649,7 @@ DescribeQuery(const char *query, double *elapsed_msec)
_("The command has no result, or the result has no columns.\n"));
}
+ SetResultVariables(results, OK);
ClearOrSaveResult(results);
return OK;
@@ -1626,6 +1677,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
bool is_pipe;
bool is_pager = false;
bool started_txn = false;
+ int64 total_tuples = 0;
int ntuples;
int fetch_count;
char fetch_cmd[64];
@@ -1663,6 +1715,8 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
results = PQexec(pset.db, buf.data);
OK = AcceptResult(results) &&
(PQresultStatus(results) == PGRES_COMMAND_OK);
+ if (!OK)
+ SetResultVariables(results, OK);
ClearOrSaveResult(results);
termPQExpBuffer(&buf);
if (!OK)
@@ -1738,6 +1792,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
OK = AcceptResult(results);
Assert(!OK);
+ SetResultVariables(results, OK);
ClearOrSaveResult(results);
break;
}
@@ -1755,6 +1810,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
*/
ntuples = PQntuples(results);
+ total_tuples += ntuples;
if (ntuples < fetch_count)
{
@@ -1816,6 +1872,21 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
ClosePager(fout);
}
+ if (OK)
+ {
+ /*
+ * We don't have a PGresult here, and even if we did it wouldn't have
+ * the right row count, so fake SetResultVariables(). In error cases,
+ * we already set the result variables above.
+ */
+ char buf[32];
+
+ SetVariable(pset.vars, "ERROR", "false");
+ SetVariable(pset.vars, "SQLSTATE", "00000");
+ snprintf(buf, sizeof(buf), INT64_FORMAT, total_tuples);
+ SetVariable(pset.vars, "ROW_COUNT", buf);
+ }
+
cleanup:
if (pset.timing)
INSTR_TIME_SET_CURRENT(before);