<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/perform.sgml,v 1.18 2002/03/22 19:20:17 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/perform.sgml,v 1.19 2002/03/24 04:31:05 tgl Exp $
-->
<chapter id="performance-tips">
<listitem>
<para>
- Estimated number of rows output by this plan node (again, without
- regard for any LIMIT).
+ Estimated number of rows output by this plan node (again, only if
+ executed to completion).
</para>
</listitem>
<para>
Here are some examples (using the regress test database after a
- vacuum analyze, and 7.2 development sources):
+ vacuum analyze, and 7.3 development sources):
<programlisting>
regression=# EXPLAIN SELECT * FROM tenk1;
-INFO: QUERY PLAN:
-
-Seq Scan on tenk1 (cost=0.00..333.00 rows=10000 width=148)
+ QUERY PLAN
+-------------------------------------------------------------
+ Seq Scan on tenk1 (cost=0.00..333.00 rows=10000 width=148)
</programlisting>
</para>
<programlisting>
regression=# EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 1000;
-INFO: QUERY PLAN:
-
-Seq Scan on tenk1 (cost=0.00..358.00 rows=1007 width=148)
+ QUERY PLAN
+------------------------------------------------------------
+ Seq Scan on tenk1 (cost=0.00..358.00 rows=1033 width=148)
+ Filter: (unique1 < 1000)
</programlisting>
The estimate of output rows has gone down because of the WHERE clause.
<programlisting>
regression=# EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 50;
-INFO: QUERY PLAN:
-
-Index Scan using tenk1_unique1 on tenk1 (cost=0.00..181.09 rows=49 width=148)
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Index Scan using tenk1_unique1 on tenk1 (cost=0.00..179.33 rows=49 width=148)
+ Index Filter: (unique1 < 50)
</programlisting>
and you will see that if we make the WHERE condition selective
<programlisting>
regression=# EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 50 AND
regression-# stringu1 = 'xxx';
-INFO: QUERY PLAN:
-
-Index Scan using tenk1_unique1 on tenk1 (cost=0.00..181.22 rows=1 width=148)
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Index Scan using tenk1_unique1 on tenk1 (cost=0.00..179.45 rows=1 width=148)
+ Index Filter: (unique1 < 50)
+ Filter: (stringu1 = 'xxx'::name)
</programlisting>
- The added clause <literal>stringu1 = 'xxx'</literal> reduces the output-rows estimate,
- but not the cost because we still have to visit the same set of tuples.
+ The added clause <literal>stringu1 = 'xxx'</literal> reduces the
+ output-rows estimate, but not the cost because we still have to visit the
+ same set of tuples. Notice that the <literal>stringu1</> clause
+ cannot be applied as an index condition (since this index is only on
+ the <literal>unique1</> column). Instead it is applied as a filter on
+ the rows retrieved by the index. Thus the cost has actually gone up
+ a little bit to reflect this extra checking.
</para>
<para>
<programlisting>
regression=# EXPLAIN SELECT * FROM tenk1 t1, tenk2 t2 WHERE t1.unique1 < 50
regression-# AND t1.unique2 = t2.unique2;
-INFO: QUERY PLAN:
-
-Nested Loop (cost=0.00..330.41 rows=49 width=296)
- -> Index Scan using tenk1_unique1 on tenk1 t1
- (cost=0.00..181.09 rows=49 width=148)
- -> Index Scan using tenk2_unique2 on tenk2 t2
- (cost=0.00..3.01 rows=1 width=148)
+ QUERY PLAN
+----------------------------------------------------------------------------
+ Nested Loop (cost=0.00..327.02 rows=49 width=296)
+ -> Index Scan using tenk1_unique1 on tenk1 t1
+ (cost=0.00..179.33 rows=49 width=148)
+ Index Filter: (unique1 < 50)
+ -> Index Scan using tenk2_unique2 on tenk2 t2
+ (cost=0.00..3.01 rows=1 width=148)
+ Index Filter: ("outer".unique2 = t2.unique2)
</programlisting>
</para>
SET VARIABLE
regression=# EXPLAIN SELECT * FROM tenk1 t1, tenk2 t2 WHERE t1.unique1 < 50
regression-# AND t1.unique2 = t2.unique2;
-INFO: QUERY PLAN:
-
-Hash Join (cost=181.22..564.83 rows=49 width=296)
- -> Seq Scan on tenk2 t2
- (cost=0.00..333.00 rows=10000 width=148)
- -> Hash (cost=181.09..181.09 rows=49 width=148)
- -> Index Scan using tenk1_unique1 on tenk1 t1
- (cost=0.00..181.09 rows=49 width=148)
+ QUERY PLAN
+--------------------------------------------------------------------------
+ Hash Join (cost=179.45..563.06 rows=49 width=296)
+ Hash Cond: ("outer".unique2 = "inner".unique2)
+ -> Seq Scan on tenk2 t2 (cost=0.00..333.00 rows=10000 width=148)
+ -> Hash (cost=179.33..179.33 rows=49 width=148)
+ -> Index Scan using tenk1_unique1 on tenk1 t1
+ (cost=0.00..179.33 rows=49 width=148)
+ Index Filter: (unique1 < 50)
</programlisting>
This plan proposes to extract the 50 interesting rows of <classname>tenk1</classname>
cost for the hash join, since we won't get any tuples out until we can
start reading <classname>tenk2</classname>. The total time estimate for the join also
includes a hefty charge for CPU time to probe the hash table
- 10000 times. Note, however, that we are NOT charging 10000 times 181.09;
+ 10000 times. Note, however, that we are NOT charging 10000 times 179.33;
the hash table setup is only done once in this plan type.
</para>
regression=# EXPLAIN ANALYZE
regression-# SELECT * FROM tenk1 t1, tenk2 t2
regression-# WHERE t1.unique1 < 50 AND t1.unique2 = t2.unique2;
-INFO: QUERY PLAN:
-
-Nested Loop (cost=0.00..330.41 rows=49 width=296) (actual time=1.31..28.90 rows=50 loops=1)
- -> Index Scan using tenk1_unique1 on tenk1 t1
- (cost=0.00..181.09 rows=49 width=148) (actual time=0.69..8.84 rows=50 loops=1)
- -> Index Scan using tenk2_unique2 on tenk2 t2
- (cost=0.00..3.01 rows=1 width=148) (actual time=0.28..0.31 rows=1 loops=50)
-Total runtime: 30.67 msec
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Nested Loop (cost=0.00..327.02 rows=49 width=296)
+ (actual time=1.18..29.82 rows=50 loops=1)
+ -> Index Scan using tenk1_unique1 on tenk1 t1
+ (cost=0.00..179.33 rows=49 width=148)
+ (actual time=0.63..8.91 rows=50 loops=1)
+ Index Filter: (unique1 < 50)
+ -> Index Scan using tenk2_unique2 on tenk2 t2
+ (cost=0.00..3.01 rows=1 width=148)
+ (actual time=0.29..0.32 rows=1 loops=50)
+ Index Filter: ("outer".unique2 = t2.unique2)
+ Total runtime: 31.60 msec
</screen>
Note that the <quote>actual time</quote> values are in milliseconds of
little larger than the total time reported for the top-level plan node.
For INSERT, UPDATE, and DELETE queries, the total run time may be
considerably larger, because it includes the time spent processing the
- output tuples. In these queries, the time for the top plan node
+ result tuples. In these queries, the time for the top plan node
essentially is the time spent computing the new tuples and/or locating
the old ones, but it doesn't include the time spent making the changes.
</para>
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/explain.sgml,v 1.16 2002/03/22 19:20:40 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/explain.sgml,v 1.17 2002/03/24 04:31:07 tgl Exp $
PostgreSQL documentation
-->
<term>VERBOSE</term>
<listitem>
<para>
- Flag to show detailed query plan.
+ Flag to show detailed query plan dump.
</para>
</listitem>
</varlistentry>
<variablelist>
<varlistentry>
- <term><computeroutput>
-INFO: QUERY PLAN:
-<replaceable>plan</replaceable>
- </computeroutput></term>
+ <term>Query plan</term>
<listitem>
<para>
- Explicit query plan from the <productname>PostgreSQL</productname> backend.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><computeroutput>
-EXPLAIN
- </computeroutput></term>
- <listitem>
- <para>
- Flag sent after query plan is shown.
+ Explicit query plan from the <productname>PostgreSQL</productname>
+ planner.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
+
+ <note>
+ <para>
+ Prior to <application>PostgreSQL</application> 7.3, the query plan
+ was emitted in the form of a NOTICE message. Now it appears as a
+ query result (formatted like a table with a single text column).
+ </para>
+ </note>
</refsect2>
</refsynopsisdiv>
are close to reality.
</para>
- <para>
- The VERBOSE option emits the full internal representation of the plan tree,
- rather than just a summary (and sends it to the postmaster log file, too).
- Usually this option is only useful for debugging
- <application>PostgreSQL</application>.
- </para>
-
<caution>
<para>
Keep in mind that the query is actually executed when ANALYZE is used.
</para>
</caution>
+ <para>
+ The VERBOSE option emits the full internal representation of the plan tree,
+ rather than just a summary.
+ Usually this option is only useful for debugging
+ <application>PostgreSQL</application>. The VERBOSE dump is either
+ pretty-printed or not, depending on the setting of the
+ <option>EXPLAIN_PRETTY_PRINT</option> configuration parameter.
+ </para>
+
<refsect2 id="R2-SQL-EXPLAIN-3">
<refsect2info>
<date>1998-04-15</date>
<para>
To show a query plan for a simple query on a table with a single
- <type>int4</type> column and 128 rows:
+ <type>int4</type> column and 10000 rows:
<programlisting>
EXPLAIN SELECT * FROM foo;
<computeroutput>
-INFO: QUERY PLAN:
-
-Seq Scan on foo (cost=0.00..2.28 rows=128 width=4)
-
-EXPLAIN
+ QUERY PLAN
+---------------------------------------------------------
+ Seq Scan on foo (cost=0.00..155.00 rows=10000 width=4)
+(1 row)
</computeroutput>
</programlisting>
</para>
<para>
- For the same table with an index to support an
- <firstterm>equijoin</firstterm> condition on the query,
+ If there is an index and we use a query with an indexable WHERE condition,
<command>EXPLAIN</command> will show a different plan:
<programlisting>
EXPLAIN SELECT * FROM foo WHERE i = 4;
<computeroutput>
-INFO: QUERY PLAN:
-
-Index Scan using fi on foo (cost=0.00..0.42 rows=1 width=4)
-
-EXPLAIN
+ QUERY PLAN
+--------------------------------------------------------------
+ Index Scan using fi on foo (cost=0.00..5.98 rows=1 width=4)
+ Index Filter: (i = 4)
+(2 rows)
</computeroutput>
</programlisting>
</para>
<para>
- And finally, for the same table with an index to support an
- <firstterm>equijoin</firstterm> condition on the query,
- <command>EXPLAIN</command> will show the following for a query
+ And here is an example of a query plan for a query
using an aggregate function:
<programlisting>
-EXPLAIN SELECT sum(i) FROM foo WHERE i = 4;
+EXPLAIN SELECT sum(i) FROM foo WHERE i < 4;
<computeroutput>
-INFO: QUERY PLAN:
-
-Aggregate (cost=0.42..0.42 rows=1 width=4)
- -> Index Scan using fi on foo (cost=0.00..0.42 rows=1 width=4)
+ QUERY PLAN
+---------------------------------------------------------------------
+ Aggregate (cost=23.93..23.93 rows=1 width=4)
+ -> Index Scan using fi on foo (cost=0.00..23.92 rows=6 width=4)
+ Index Filter: (i < 10)
+(3 rows)
</computeroutput>
</programlisting>
</para>
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.125 2002/03/22 19:20:22 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.126 2002/03/24 04:31:05 tgl Exp $
-->
<appendix id="release">
worries about funny characters.
-->
<literallayout><![CDATA[
+EXPLAIN output comes out as a query result, not a NOTICE message
DOMAINs (types that are constrained versions of base types)
Access privileges on functions
Access privileges on procedural languages
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.109 2002/03/22 19:20:28 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.110 2002/03/24 04:31:06 tgl Exp $
-->
<Chapter Id="runtime">
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>EXPLAIN_PRETTY_PRINT</varname> (<type>boolean</type>)</term>
+ <listitem>
+ <para>
+ Determines whether <command>EXPLAIN VERBOSE</> uses the indented
+ or non-indented format for displaying detailed querytree dumps.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>HOSTNAME_LOOKUP</varname> (<type>boolean</type>)</term>
<listitem>
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.73 2002/03/22 02:56:31 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.74 2002/03/24 04:31:07 tgl Exp $
*
*/
#include "postgres.h"
+#include "access/heapam.h"
+#include "catalog/pg_type.h"
#include "commands/explain.h"
#include "executor/instrument.h"
#include "lib/stringinfo.h"
#include "rewrite/rewriteHandler.h"
#include "tcop/pquery.h"
#include "utils/builtins.h"
+#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/relcache.h"
List *rtable; /* range table */
} ExplainState;
+typedef struct TextOutputState
+{
+ TupleDesc tupdesc;
+ DestReceiver *destfunc;
+} TextOutputState;
+
static StringInfo Explain_PlanToString(Plan *plan, ExplainState *es);
-static void ExplainOneQuery(Query *query, bool verbose, bool analyze,
- CommandDest dest);
+static void ExplainOneQuery(Query *query, ExplainStmt *stmt,
+ TextOutputState *tstate);
static void explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
int indent, ExplainState *es);
static void show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
const char *inner_name, int inner_varno, Plan *inner_plan,
StringInfo str, int indent, ExplainState *es);
static Node *make_ors_ands_explicit(List *orclauses);
+static TextOutputState *begin_text_output(CommandDest dest, char *title);
+static void do_text_output(TextOutputState *tstate, char *aline);
+static void do_text_output_multiline(TextOutputState *tstate, char *text);
+static void end_text_output(TextOutputState *tstate);
/* Convert a null string pointer into "<>" */
#define stringStringInfo(s) (((s) == NULL) ? "<>" : (s))
/*
* ExplainQuery -
- * print out the execution plan for a given query
+ * execute an EXPLAIN command
*/
void
-ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
+ExplainQuery(ExplainStmt *stmt, CommandDest dest)
{
+ Query *query = stmt->query;
+ TextOutputState *tstate;
List *rewritten;
List *l;
- /* rewriter and planner may not work in aborted state? */
- if (IsAbortedTransactionBlockState())
- {
- elog(WARNING, "(transaction aborted): %s",
- "queries ignored until END");
- return;
- }
+ tstate = begin_text_output(dest, "QUERY PLAN");
- /* rewriter will not cope with utility statements */
if (query->commandType == CMD_UTILITY)
{
- elog(NOTICE, "Utility statements have no plan structure");
- return;
+ /* rewriter will not cope with utility statements */
+ do_text_output(tstate, "Utility statements have no plan structure");
}
-
- /* Rewrite through rule system */
- rewritten = QueryRewrite(query);
-
- /* In the case of an INSTEAD NOTHING, tell at least that */
- if (rewritten == NIL)
+ else
{
- elog(NOTICE, "Query rewrites to nothing");
- return;
+ /* Rewrite through rule system */
+ rewritten = QueryRewrite(query);
+
+ if (rewritten == NIL)
+ {
+ /* In the case of an INSTEAD NOTHING, tell at least that */
+ do_text_output(tstate, "Query rewrites to nothing");
+ }
+ else
+ {
+ /* Explain every plan */
+ foreach(l, rewritten)
+ {
+ ExplainOneQuery(lfirst(l), stmt, tstate);
+ /* put a blank line between plans */
+ if (lnext(l) != NIL)
+ do_text_output(tstate, "");
+ }
+ }
}
- /* Explain every plan */
- foreach(l, rewritten)
- ExplainOneQuery(lfirst(l), verbose, analyze, dest);
+ end_text_output(tstate);
}
/*
* print out the execution plan for one query
*/
static void
-ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
+ExplainOneQuery(Query *query, ExplainStmt *stmt, TextOutputState *tstate)
{
Plan *plan;
ExplainState *es;
if (query->commandType == CMD_UTILITY)
{
if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
- elog(INFO, "QUERY PLAN:\n\nNOTIFY\n");
+ do_text_output(tstate, "NOTIFY");
else
- elog(INFO, "QUERY PLAN:\n\nUTILITY\n");
+ do_text_output(tstate, "UTILITY");
return;
}
return;
/* Execute the plan for statistics if asked for */
- if (analyze)
+ if (stmt->analyze)
{
struct timeval starttime;
struct timeval endtime;
es->printCost = true; /* default */
- if (verbose)
+ if (stmt->verbose)
es->printNodes = true;
es->rtable = query->rtable;
if (es->printNodes)
{
char *s;
+ char *f;
s = nodeToString(plan);
if (s)
{
- elog(INFO, "QUERY DUMP:\n\n%s", s);
+ if (Explain_pretty_print)
+ f = pretty_format_node_dump(s);
+ else
+ f = format_node_dump(s);
pfree(s);
+ do_text_output_multiline(tstate, f);
+ pfree(f);
+ if (es->printCost)
+ do_text_output(tstate, ""); /* separator line */
}
}
StringInfo str;
str = Explain_PlanToString(plan, es);
- if (analyze)
+ if (stmt->analyze)
appendStringInfo(str, "Total runtime: %.2f msec\n",
1000.0 * totaltime);
- elog(INFO, "QUERY PLAN:\n\n%s", str->data);
+ do_text_output_multiline(tstate, str->data);
pfree(str->data);
pfree(str);
}
- if (es->printNodes)
- pprint(plan); /* display in postmaster log file */
-
pfree(es);
}
return (Node *) make_orclause(args);
}
}
+
+
+/*
+ * Functions for sending text to the frontend (or other specified destination)
+ * as though it is a SELECT result.
+ *
+ * We tell the frontend that the table structure is a single TEXT column.
+ */
+
+static TextOutputState *
+begin_text_output(CommandDest dest, char *title)
+{
+ TextOutputState *tstate;
+ TupleDesc tupdesc;
+
+ tstate = (TextOutputState *) palloc(sizeof(TextOutputState));
+
+ /* need a tuple descriptor representing a single TEXT column */
+ tupdesc = CreateTemplateTupleDesc(1);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, title,
+ TEXTOID, -1, 0, false);
+
+ tstate->tupdesc = tupdesc;
+ tstate->destfunc = DestToFunction(dest);
+
+ (*tstate->destfunc->setup) (tstate->destfunc, (int) CMD_SELECT,
+ NULL, tupdesc);
+
+ return tstate;
+}
+
+/* write a single line of text */
+static void
+do_text_output(TextOutputState *tstate, char *aline)
+{
+ HeapTuple tuple;
+ Datum values[1];
+ char nulls[1];
+
+ /* form a tuple and send it to the receiver */
+ values[0] = DirectFunctionCall1(textin, CStringGetDatum(aline));
+ nulls[0] = ' ';
+ tuple = heap_formtuple(tstate->tupdesc, values, nulls);
+ (*tstate->destfunc->receiveTuple) (tuple,
+ tstate->tupdesc,
+ tstate->destfunc);
+ pfree(DatumGetPointer(values[0]));
+ heap_freetuple(tuple);
+}
+
+/* write a chunk of text, breaking at newline characters */
+/* NB: scribbles on its input! */
+static void
+do_text_output_multiline(TextOutputState *tstate, char *text)
+{
+ while (*text)
+ {
+ char *eol;
+
+ eol = strchr(text, '\n');
+ if (eol)
+ *eol++ = '\0';
+ else
+ eol = text + strlen(text);
+ do_text_output(tstate, text);
+ text = eol;
+ }
+}
+
+static void
+end_text_output(TextOutputState *tstate)
+{
+ (*tstate->destfunc->cleanup) (tstate->destfunc);
+ pfree(tstate);
+}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.53 2002/03/22 02:56:32 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.54 2002/03/24 04:31:07 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
#include "access/printtup.h"
#include "catalog/pg_type.h"
+#include "lib/stringinfo.h"
#include "nodes/print.h"
#include "optimizer/clauses.h"
#include "parser/parsetree.h"
print(void *obj)
{
char *s;
+ char *f;
s = nodeToString(obj);
- printf("%s\n", s);
- fflush(stdout);
+ f = format_node_dump(s);
pfree(s);
+ printf("%s\n", f);
+ fflush(stdout);
+ pfree(f);
}
/*
- * pretty print hack extraordinaire. -ay 10/94
+ * pprint
+ * pretty-print contents of Node to stdout
*/
void
pprint(void *obj)
{
-#define INDENTSTOP 3
-#define MAXINDENT 60
-#define LINELEN 80
char *s;
+ char *f;
+
+ s = nodeToString(obj);
+ f = pretty_format_node_dump(s);
+ pfree(s);
+ printf("%s\n", f);
+ fflush(stdout);
+ pfree(f);
+}
+
+/*
+ * elog_node_display
+ * send pretty-printed contents of Node to postmaster log
+ */
+void
+elog_node_display(int lev, const char *title, void *obj, bool pretty)
+{
+ char *s;
+ char *f;
+
+ s = nodeToString(obj);
+ if (pretty)
+ f = pretty_format_node_dump(s);
+ else
+ f = format_node_dump(s);
+ pfree(s);
+ elog(lev, "%s:\n%s", title, f);
+ pfree(f);
+}
+
+/*
+ * Format a nodeToString output for display on a terminal.
+ *
+ * The result is a palloc'd string.
+ *
+ * This version just tries to break at whitespace.
+ */
+char *
+format_node_dump(const char *dump)
+{
+#define LINELEN 78
+ char line[LINELEN+1];
+ StringInfoData str;
int i;
- char line[LINELEN];
+ int j;
+ int k;
+
+ initStringInfo(&str);
+ i = 0;
+ for (;;)
+ {
+ for (j = 0; j < LINELEN && dump[i] != '\0'; i++, j++)
+ line[j] = dump[i];
+ if (dump[i] == '\0')
+ break;
+ if (dump[i] == ' ')
+ {
+ /* ok to break at adjacent space */
+ i++;
+ }
+ else
+ {
+ for (k = j-1; k > 0; k--)
+ if (line[k] == ' ')
+ break;
+ if (k > 0)
+ {
+ /* back up; will reprint all after space */
+ i -= (j-k-1);
+ j = k;
+ }
+ }
+ line[j] = '\0';
+ appendStringInfo(&str, "%s\n", line);
+ }
+ if (j > 0)
+ {
+ line[j] = '\0';
+ appendStringInfo(&str, "%s\n", line);
+ }
+ return str.data;
+#undef LINELEN
+}
+
+/*
+ * Format a nodeToString output for display on a terminal.
+ *
+ * The result is a palloc'd string.
+ *
+ * This version tries to indent intelligently.
+ */
+char *
+pretty_format_node_dump(const char *dump)
+{
+#define INDENTSTOP 3
+#define MAXINDENT 60
+#define LINELEN 78
+ char line[LINELEN+1];
+ StringInfoData str;
int indentLev;
int indentDist;
+ int i;
int j;
- s = nodeToString(obj);
-
+ initStringInfo(&str);
indentLev = 0; /* logical indent level */
indentDist = 0; /* physical indent distance */
i = 0;
{
for (j = 0; j < indentDist; j++)
line[j] = ' ';
- for (; j < LINELEN-1 && s[i] != '\0'; i++, j++)
+ for (; j < LINELEN && dump[i] != '\0'; i++, j++)
{
- line[j] = s[i];
+ line[j] = dump[i];
switch (line[j])
{
case '}':
{
/* print data before the } */
line[j] = '\0';
- printf("%s\n", line);
+ appendStringInfo(&str, "%s\n", line);
}
/* print the } at indentDist */
- line[indentDist] = '\0';
- printf("%s}\n", line);
+ line[indentDist] = '}';
+ line[indentDist+1] = '\0';
+ appendStringInfo(&str, "%s\n", line);
/* outdent */
if (indentLev > 0)
{
case ')':
/* force line break after ')' */
line[j + 1] = '\0';
- printf("%s\n", line);
+ appendStringInfo(&str, "%s\n", line);
j = indentDist - 1;
break;
case '{':
if (j != indentDist)
{
line[j] = '\0';
- printf("%s\n", line);
+ appendStringInfo(&str, "%s\n", line);
}
/* indent */
indentLev++;
indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
for (j = 0; j < indentDist; j++)
line[j] = ' ';
- line[j] = s[i];
+ line[j] = dump[i];
break;
case ':':
/* force line break before : */
if (j != indentDist)
{
line[j] = '\0';
- printf("%s\n", line);
+ appendStringInfo(&str, "%s\n", line);
}
j = indentDist;
- line[j] = s[i];
+ line[j] = dump[i];
break;
}
}
line[j] = '\0';
- if (s[i] == '\0')
+ if (dump[i] == '\0')
break;
- printf("%s\n", line);
+ appendStringInfo(&str, "%s\n", line);
}
- if (j != 0)
- printf("%s\n", line);
- fflush(stdout);
- pfree(s);
+ if (j > 0)
+ appendStringInfo(&str, "%s\n", line);
+ return str.data;
+#undef INDENTSTOP
+#undef MAXINDENT
+#undef LINELEN
}
/*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.259 2002/03/22 02:56:35 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.260 2002/03/24 04:31:07 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
querytree = (Query *) lfirst(list_item);
if (Debug_print_parse)
- {
- if (Debug_pretty_print)
- {
- elog(LOG, "parse tree:");
- nodeDisplay(querytree);
- }
- else
- elog(LOG, "parse tree: %s", nodeToString(querytree));
- }
+ elog_node_display(LOG, "parse tree", querytree,
+ Debug_pretty_print);
if (querytree->commandType == CMD_UTILITY)
{
#endif
if (Debug_print_rewritten)
- {
- if (Debug_pretty_print)
- {
- elog(LOG, "rewritten parse tree:");
- foreach(list_item, querytree_list)
- {
- querytree = (Query *) lfirst(list_item);
- nodeDisplay(querytree);
- printf("\n");
- }
- }
- else
- {
- elog(LOG, "rewritten parse tree:");
- foreach(list_item, querytree_list)
- {
- querytree = (Query *) lfirst(list_item);
- elog(LOG, "%s", nodeToString(querytree));
- }
- }
- }
+ elog_node_display(LOG, "rewritten parse tree", querytree_list,
+ Debug_pretty_print);
return querytree_list;
}
* Print plan if debugging.
*/
if (Debug_print_plan)
- {
- if (Debug_pretty_print)
- {
- elog(LOG, "plan:");
- nodeDisplay(plan);
- }
- else
- elog(LOG, "plan: %s", nodeToString(plan));
- }
+ elog_node_display(LOG, "plan", plan, Debug_pretty_print);
return plan;
}
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
- puts("$Revision: 1.259 $ $Date: 2002/03/22 02:56:35 $\n");
+ puts("$Revision: 1.260 $ $Date: 2002/03/24 04:31:07 $\n");
}
/*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.138 2002/03/22 02:56:35 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.139 2002/03/24 04:31:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
break;
case T_ExplainStmt:
- {
- ExplainStmt *stmt = (ExplainStmt *) parsetree;
-
- ExplainQuery(stmt->query, stmt->verbose, stmt->analyze, dest);
- }
+ ExplainQuery((ExplainStmt *) parsetree, dest);
break;
#ifdef NOT_USED
* Support for grand unified configuration scheme, including SET
* command, configuration file, and command line options.
*
- * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.62 2002/03/06 06:10:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.63 2002/03/24 04:31:08 tgl Exp $
*
* Copyright 2000 by PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>.
* together */
bool Show_btree_build_stats = false;
+bool Explain_pretty_print = true;
+
bool SQL_inheritance = true;
bool Australian_timezones = false;
},
#endif
+ {
+ "explain_pretty_print", PGC_USERSET, PGC_S_DEFAULT, &Explain_pretty_print, true, NULL
+ },
+
{
"stats_start_collector", PGC_POSTMASTER, PGC_S_DEFAULT, &pgstat_collect_startcollector, true, NULL
},
#debug_print_plan = false
#debug_pretty_print = false
+#explain_pretty_print = true
+
# requires USE_ASSERT_CHECKING
#debug_assertions = true
*
* Copyright 2000 by PostgreSQL Global Development Group
*
- * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.45 2002/03/07 20:48:41 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.46 2002/03/24 04:31:08 tgl Exp $
*/
/*----------------------------------------------------------------------
"show_executor_stats",
"show_query_stats",
"trace_notify",
+ "explain_pretty_print",
"sql_inheritance",
"australian_timezones",
"password_encryption",
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California
*
- * $Id: explain.h,v 1.15 2001/11/05 17:46:33 momjian Exp $
+ * $Id: explain.h,v 1.16 2002/03/24 04:31:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "nodes/parsenodes.h"
#include "tcop/dest.h"
-extern void ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest);
+extern void ExplainQuery(ExplainStmt *stmt, CommandDest dest);
#endif /* EXPLAIN_H */
/*-------------------------------------------------------------------------
*
- * execnodes.h
- * definitions for executor state nodes
+ * print.h
+ * definitions for nodes/print.c
*
*
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: print.h,v 1.16 2001/11/05 17:46:34 momjian Exp $
+ * $Id: print.h,v 1.17 2002/03/24 04:31:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "nodes/parsenodes.h"
#include "nodes/plannodes.h"
-/*
- * nodes/{outfuncs.c,print.c}
- */
+
#define nodeDisplay pprint
extern void print(void *obj);
extern void pprint(void *obj);
+extern void elog_node_display(int lev, const char *title,
+ void *obj, bool pretty);
+extern char *format_node_dump(const char *dump);
+extern char *pretty_format_node_dump(const char *dump);
extern void print_rt(List *rtable);
extern void print_expr(Node *expr, List *rtable);
extern void print_pathkeys(List *pathkeys, List *rtable);
* External declarations pertaining to backend/utils/misc/guc.c and
* backend/utils/misc/guc-file.l
*
- * $Id: guc.h,v 1.15 2002/03/01 22:45:18 petere Exp $
+ * $Id: guc.h,v 1.16 2002/03/24 04:31:09 tgl Exp $
*/
#ifndef GUC_H
#define GUC_H
extern bool Show_query_stats;
extern bool Show_btree_build_stats;
+extern bool Explain_pretty_print;
+
extern bool SQL_inheritance;
extern bool Australian_timezones;