summaryrefslogtreecommitdiff
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.c131
1 files changed, 128 insertions, 3 deletions
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index a41932ff275..b99705886fa 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -29,6 +29,7 @@
#include "fe_utils/mbprint.h"
+static bool DescribeQuery(const char *query, double *elapsed_msec);
static bool ExecQueryUsingCursor(const char *query, double *elapsed_msec);
static bool command_no_begin(const char *query);
static bool is_select_command(const char *query);
@@ -1323,8 +1324,15 @@ SendQuery(const char *query)
}
}
- if (pset.fetch_count <= 0 || pset.gexec_flag ||
- pset.crosstab_flag || !is_select_command(query))
+ if (pset.gdesc_flag)
+ {
+ /* Describe query's result columns, without executing it */
+ OK = DescribeQuery(query, &elapsed_msec);
+ ResetCancelConn();
+ results = NULL; /* PQclear(NULL) does nothing */
+ }
+ else if (pset.fetch_count <= 0 || pset.gexec_flag ||
+ pset.crosstab_flag || !is_select_command(query))
{
/* Default fetch-it-all-and-print mode */
instr_time before,
@@ -1467,6 +1475,9 @@ sendquery_cleanup:
pset.gset_prefix = NULL;
}
+ /* reset \gdesc trigger */
+ pset.gdesc_flag = false;
+
/* reset \gexec trigger */
pset.gexec_flag = false;
@@ -1483,6 +1494,118 @@ sendquery_cleanup:
/*
+ * DescribeQuery: describe the result columns of a query, without executing it
+ *
+ * Returns true if the operation executed successfully, false otherwise.
+ *
+ * If pset.timing is on, total query time (exclusive of result-printing) is
+ * stored into *elapsed_msec.
+ */
+static bool
+DescribeQuery(const char *query, double *elapsed_msec)
+{
+ PGresult *results;
+ bool OK;
+ instr_time before,
+ after;
+
+ *elapsed_msec = 0;
+
+ if (pset.timing)
+ INSTR_TIME_SET_CURRENT(before);
+
+ /*
+ * To parse the query but not execute it, we prepare it, using the unnamed
+ * prepared statement. This is invisible to psql users, since there's no
+ * way to access the unnamed prepared statement from psql user space. The
+ * next Parse or Query protocol message would overwrite the statement
+ * anyway. (So there's no great need to clear it when done, which is a
+ * good thing because libpq provides no easy way to do that.)
+ */
+ results = PQprepare(pset.db, "", query, 0, NULL);
+ if (PQresultStatus(results) != PGRES_COMMAND_OK)
+ {
+ psql_error("%s", PQerrorMessage(pset.db));
+ ClearOrSaveResult(results);
+ return false;
+ }
+ PQclear(results);
+
+ results = PQdescribePrepared(pset.db, "");
+ OK = AcceptResult(results) &&
+ (PQresultStatus(results) == PGRES_COMMAND_OK);
+ if (OK && results)
+ {
+ if (PQnfields(results) > 0)
+ {
+ PQExpBufferData buf;
+ int i;
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT name AS \"%s\", pg_catalog.format_type(tp, tpm) AS \"%s\"\n"
+ "FROM (VALUES ",
+ gettext_noop("Column"),
+ gettext_noop("Type"));
+
+ for (i = 0; i < PQnfields(results); i++)
+ {
+ const char *name;
+ char *escname;
+
+ if (i > 0)
+ appendPQExpBufferStr(&buf, ",");
+
+ name = PQfname(results, i);
+ escname = PQescapeLiteral(pset.db, name, strlen(name));
+
+ if (escname == NULL)
+ {
+ psql_error("%s", PQerrorMessage(pset.db));
+ PQclear(results);
+ termPQExpBuffer(&buf);
+ return false;
+ }
+
+ appendPQExpBuffer(&buf, "(%s, '%u'::pg_catalog.oid, %d)",
+ escname,
+ PQftype(results, i),
+ PQfmod(results, i));
+
+ PQfreemem(escname);
+ }
+
+ appendPQExpBufferStr(&buf, ") s(name, tp, tpm)");
+ PQclear(results);
+
+ results = PQexec(pset.db, buf.data);
+ OK = AcceptResult(results);
+
+ if (pset.timing)
+ {
+ INSTR_TIME_SET_CURRENT(after);
+ INSTR_TIME_SUBTRACT(after, before);
+ *elapsed_msec += INSTR_TIME_GET_MILLISEC(after);
+ }
+
+ if (OK && results)
+ OK = PrintQueryResults(results);
+
+ termPQExpBuffer(&buf);
+ }
+ else
+ fprintf(pset.queryFout,
+ _("The command has no result, or the result has no columns.\n"));
+ }
+
+ ClearOrSaveResult(results);
+
+ return OK;
+}
+
+
+/*
* ExecQueryUsingCursor: run a SELECT-like query using a cursor
*
* This feature allows result sets larger than RAM to be dealt with.
@@ -1627,7 +1750,9 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
break;
}
- /* Note we do not deal with \gexec or \crosstabview modes here */
+ /*
+ * Note we do not deal with \gdesc, \gexec or \crosstabview modes here
+ */
ntuples = PQntuples(results);