<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/perform.sgml,v 1.9 2001/09/13 15:55:23 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/perform.sgml,v 1.10 2001/09/18 01:59:05 tgl Exp $
-->
<chapter id="performance-tips">
regression=# explain select * from tenk1 where unique1 < 1000;
NOTICE: QUERY PLAN:
-Seq Scan on tenk1 (cost=0.00..358.00 rows=1003 width=148)
+Seq Scan on tenk1 (cost=0.00..358.00 rows=1007 width=148)
</programlisting>
The estimate of output rows has gone down because of the WHERE clause.
regression=# explain select * from tenk1 where unique1 < 50;
NOTICE: QUERY PLAN:
-Index Scan using tenk1_unique1 on tenk1 (cost=0.00..173.32 rows=47 width=148)
+Index Scan using tenk1_unique1 on tenk1 (cost=0.00..181.09 rows=49 width=148)
</programlisting>
and you will see that if we make the WHERE condition selective
regression-# stringu1 = 'xxx';
NOTICE: QUERY PLAN:
-Index Scan using tenk1_unique1 on tenk1 (cost=0.00..173.44 rows=1 width=148)
+Index Scan using tenk1_unique1 on tenk1 (cost=0.00..181.22 rows=1 width=148)
</programlisting>
The added clause <literal>stringu1 = 'xxx'</literal> reduces the output-rows estimate,
regression-# and t1.unique2 = t2.unique2;
NOTICE: QUERY PLAN:
-Nested Loop (cost=0.00..269.11 rows=47 width=296)
+Nested Loop (cost=0.00..330.41 rows=49 width=296)
-> Index Scan using tenk1_unique1 on tenk1 t1
- (cost=0.00..173.32 rows=47 width=148)
+ (cost=0.00..181.09 rows=49 width=148)
-> Index Scan using tenk2_unique2 on tenk2 t2
- (cost=0.00..2.01 rows=1 width=148)
+ (cost=0.00..3.01 rows=1 width=148)
</programlisting>
</para>
same inner-scan plan and costs that we'd get from, say, <literal>explain select
* from tenk2 where unique2 = 42</literal>. The costs of the loop node are then set
on the basis of the cost of the outer scan, plus one repetition of the
- inner scan for each outer tuple (47 * 2.01, here), plus a little CPU
+ inner scan for each outer tuple (49 * 3.01, here), plus a little CPU
time for join processing.
</para>
regression-# and t1.unique2 = t2.unique2;
NOTICE: QUERY PLAN:
-Hash Join (cost=173.44..557.03 rows=47 width=296)
+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=173.32..173.32 rows=47 width=148)
+ -> Hash (cost=181.09..181.09 rows=49 width=148)
-> Index Scan using tenk1_unique1 on tenk1 t1
- (cost=0.00..173.32 rows=47 width=148)
+ (cost=0.00..181.09 rows=49 width=148)
</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 173.32;
+ 10000 times. Note, however, that we are NOT charging 10000 times 181.09;
the hash table setup is only done once in this plan type.
</para>
+ <para>
+ It is possible to check on the accuracy of the planner's estimated costs
+ by using EXPLAIN ANALYZE. This command actually executes the query,
+ and then displays the true runtime accumulated within each plan node
+ along with the same estimated costs that a plain EXPLAIN shows.
+ For example, we might get a result like this:
+
+ <programlisting>
+regression=# explain analyze select * from tenk1 t1, tenk2 t2 where t1.unique1 < 50
+regression-# and t1.unique2 = t2.unique2;
+NOTICE: 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
+ </programlisting>
+
+ Note that the <quote>actual time</quote> values are in milliseconds of
+ real time, whereas the <quote>cost</quote> estimates are expressed in
+ arbitrary units of disk fetches; so they are unlikely to match up.
+ The thing to pay attention to is the ratios.
+ </para>
+
+ <para>
+ In some query plans, it is possible for a subplan node to be executed more
+ than once. For example, the inner indexscan is executed once per outer
+ tuple in the above nested-loop plan. In such cases, the
+ <quote>loops</quote> value reports the
+ total number of executions of the node, and the actual time and rows
+ values shown are averages per-execution. This is done to make the numbers
+ comparable with the way that the cost estimates are shown. Multiply by
+ the <quote>loops</quote> value to get the total time actually spent in
+ the node.
+ </para>
+
+ <para>
+ The <quote>total runtime</quote> shown by EXPLAIN ANALYZE includes
+ executor startup and shutdown time, as well as time spent processing
+ the result tuples. It does not include parsing, rewriting, or planning
+ time. For a SELECT query, the total runtime will normally be just a
+ little larger than the total time reported for the top-level plan node.
+ For INSERT, UPDATE, and DELETE queries, the total runtime may be
+ considerably larger, because it includes the time spent processing the
+ output 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>
+
<para>
It is worth noting that EXPLAIN results should not be extrapolated
to situations other than the one you are actually testing; for example,
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/explain.sgml,v 1.12 2001/09/03 12:57:50 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/explain.sgml,v 1.13 2001/09/18 01:59:05 tgl Exp $
Postgres documentation
-->
<date>1999-07-20</date>
</refsynopsisdivinfo>
<synopsis>
-EXPLAIN [ VERBOSE ] <replaceable class="PARAMETER">query</replaceable>
+EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceable>
</synopsis>
<refsect2 id="R2-SQL-EXPLAIN-1">
<para>
<variablelist>
+ <varlistentry>
+ <term>ANALYZE</term>
+ <listitem>
+ <para>
+ Flag to carry out the query and show actual runtimes.
+ </para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term>VERBOSE</term>
<listitem>
costs to estimate which plan is really the cheapest.
</para>
+ <para>
+ The ANALYZE option causes the query to be actually executed, not only
+ planned. The total elapsed time expended within each plan node (in
+ milliseconds) and total number of rows it actually returned are added to
+ the display. This is useful for seeing whether the planner's estimates
+ 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 Postgres.
</para>
+ <caution>
+ <para>
+ Keep in mind that the query is actually executed when ANALYZE is used.
+ Although <command>EXPLAIN</command> will discard any output that a SELECT
+ would return,
+ other side-effects of the query will happen as usual.
+ If you wish to use <command>EXPLAIN ANALYZE</command> on an INSERT,
+ UPDATE, or DELETE query without letting the query affect your data,
+ use this approach:
+ <programlisting>
+BEGIN;
+EXPLAIN ANALYZE ...;
+ROLLBACK;
+ </programlisting>
+ </para>
+ </caution>
+
<refsect2 id="R2-SQL-EXPLAIN-3">
<refsect2info>
<date>1998-04-15</date>
<para>
There is only sparse documentation on the optimizer's use of cost
information in <productname>Postgres</productname>.
- General information on cost estimation for query optimization
- can be found in database textbooks.
- Refer to the <citetitle>Programmer's Guide</citetitle>
- in the chapters on indexes and the genetic query optimizer for
- more information.
+ Refer to the <citetitle>User's Guide</citetitle> and
+ <citetitle>Programmer's Guide</citetitle> for more information.
</para>
</refsect2>
</refsect1>
* 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.65 2001/03/22 03:59:22 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.66 2001/09/18 01:59:06 tgl Exp $
*
*/
#include "postgres.h"
#include "commands/explain.h"
+#include "executor/instrument.h"
#include "lib/stringinfo.h"
#include "nodes/print.h"
#include "optimizer/planner.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
+#include "tcop/pquery.h"
#include "utils/relcache.h"
typedef struct ExplainState
List *rtable; /* range table */
} ExplainState;
-static char *Explain_PlanToString(Plan *plan, ExplainState *es);
-static void ExplainOneQuery(Query *query, bool verbose, CommandDest dest);
+static StringInfo Explain_PlanToString(Plan *plan, ExplainState *es);
+static void ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest);
/* Convert a null string pointer into "<>" */
#define stringStringInfo(s) (((s) == NULL) ? "<>" : (s))
*
*/
void
-ExplainQuery(Query *query, bool verbose, CommandDest dest)
+ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
{
List *rewritten;
List *l;
/* Explain every plan */
foreach(l, rewritten)
- ExplainOneQuery(lfirst(l), verbose, dest);
+ ExplainOneQuery(lfirst(l), verbose, analyze, dest);
}
/*
*
*/
static void
-ExplainOneQuery(Query *query, bool verbose, CommandDest dest)
+ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
{
- char *s;
Plan *plan;
ExplainState *es;
+ double totaltime = 0;
/* planner will not cope with utility statements */
if (query->commandType == CMD_UTILITY)
if (plan == NULL)
return;
+ /* Execute the plan for statistics if asked for */
+ if (analyze)
+ {
+ struct timeval starttime;
+ struct timeval endtime;
+
+ /*
+ * Set up the instrumentation for the top node.
+ * This will cascade during plan initialisation
+ */
+ plan->instrument = InstrAlloc();
+
+ gettimeofday(&starttime, NULL);
+ ProcessQuery(query, plan, None);
+ CommandCounterIncrement();
+ gettimeofday(&endtime, NULL);
+
+ endtime.tv_sec -= starttime.tv_sec;
+ endtime.tv_usec -= starttime.tv_usec;
+ while (endtime.tv_usec < 0)
+ {
+ endtime.tv_usec += 1000000;
+ endtime.tv_sec--;
+ }
+ totaltime = (double) endtime.tv_sec +
+ (double) endtime.tv_usec / 1000000.0;
+ }
+
es = (ExplainState *) palloc(sizeof(ExplainState));
MemSet(es, 0, sizeof(ExplainState));
if (es->printNodes)
{
+ char *s;
+
s = nodeToString(plan);
if (s)
{
if (es->printCost)
{
- s = Explain_PlanToString(plan, es);
- if (s)
- {
- elog(NOTICE, "QUERY PLAN:\n\n%s", s);
- pfree(s);
- }
+ StringInfo str;
+
+ str = Explain_PlanToString(plan, es);
+ if (analyze)
+ appendStringInfo(str, "Total runtime: %.2f msec\n",
+ 1000.0 * totaltime);
+ elog(NOTICE, "QUERY PLAN:\n\n%s", str->data);
+ pfree(str->data);
+ pfree(str);
}
if (es->printNodes)
appendStringInfo(str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
plan->startup_cost, plan->total_cost,
plan->plan_rows, plan->plan_width);
+
+ if ( plan->instrument && plan->instrument->nloops > 0 )
+ {
+ double nloops = plan->instrument->nloops;
+
+ appendStringInfo(str, " (actual time=%.2f..%.2f rows=%.0f loops=%.0f)",
+ 1000.0 * plan->instrument->startup / nloops,
+ 1000.0 * plan->instrument->total / nloops,
+ plan->instrument->ntuples / nloops,
+ plan->instrument->nloops);
+ }
}
appendStringInfo(str, "\n");
}
}
-static char *
+static StringInfo
Explain_PlanToString(Plan *plan, ExplainState *es)
{
- StringInfoData str;
+ StringInfo str = makeStringInfo();
- /* see stringinfo.h for an explanation of this maneuver */
- initStringInfo(&str);
if (plan != NULL)
- explain_outNode(&str, plan, 0, es);
- return str.data;
+ explain_outNode(str, plan, 0, es);
+ return str;
}
# Makefile for executor
#
# IDENTIFICATION
-# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.16 2000/10/26 21:35:15 tgl Exp $
+# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.17 2001/09/18 01:59:06 tgl Exp $
#
#-------------------------------------------------------------------------
OBJS = execAmi.o execFlatten.o execJunk.o execMain.o \
execProcnode.o execQual.o execScan.o execTuples.o \
- execUtils.o functions.o nodeAppend.o nodeAgg.o nodeHash.o \
+ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o nodeHash.o \
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSetOp.o nodeSort.o \
nodeUnique.o nodeLimit.o nodeGroup.o nodeSubplan.o \
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: execAmi.c,v 1.58 2001/03/22 06:16:12 momjian Exp $
+ * $Id: execAmi.c,v 1.59 2001/09/18 01:59:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "access/heapam.h"
#include "catalog/heap.h"
#include "executor/execdebug.h"
+#include "executor/instrument.h"
#include "executor/nodeAgg.h"
#include "executor/nodeAppend.h"
#include "executor/nodeGroup.h"
void
ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
{
+ if (node->instrument)
+ InstrEndLoop(node->instrument);
if (node->chgParam != NULL) /* Wow! */
{
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.147 2001/09/17 00:29:10 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.148 2001/09/18 01:59:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* shut down the node-type-specific query processing
*/
- ExecEndNode(plan, plan);
+ ExecEndNode(plan, NULL);
/*
* destroy the executor "tuple" table.
/*
* Execute the plan and obtain a tuple
*/
- /* at the top level, the parent of a plan (2nd arg) is itself */
lnext: ;
if (estate->es_useEvalPlan)
{
slot = EvalPlanQualNext(estate);
if (TupIsNull(slot))
- slot = ExecProcNode(plan, plan);
+ slot = ExecProcNode(plan, NULL);
}
else
- slot = ExecProcNode(plan, plan);
+ slot = ExecProcNode(plan, NULL);
/*
* if the tuple is null, then we assume there is nothing more to
oldepq = (evalPlanQual *) epqstate->es_evalPlanQual;
Assert(oldepq->rti != 0);
/* stop execution */
- ExecEndNode(epq->plan, epq->plan);
+ ExecEndNode(epq->plan, NULL);
ExecDropTupleTable(epqstate->es_tupleTable, true);
epqstate->es_tupleTable = NULL;
heap_freetuple(epqstate->es_evTuple[epq->rti - 1]);
if (endNode)
{
/* stop execution */
- ExecEndNode(epq->plan, epq->plan);
+ ExecEndNode(epq->plan, NULL);
ExecDropTupleTable(epqstate->es_tupleTable, true);
epqstate->es_tupleTable = NULL;
}
Assert(epq->rti != 0);
lpqnext:;
- slot = ExecProcNode(epq->plan, epq->plan);
+ slot = ExecProcNode(epq->plan, NULL);
/*
* No more tuples for this PQ. Continue previous one.
if (TupIsNull(slot))
{
/* stop execution */
- ExecEndNode(epq->plan, epq->plan);
+ ExecEndNode(epq->plan, NULL);
ExecDropTupleTable(epqstate->es_tupleTable, true);
epqstate->es_tupleTable = NULL;
heap_freetuple(epqstate->es_evTuple[epq->rti - 1]);
for (;;)
{
/* stop execution */
- ExecEndNode(epq->plan, epq->plan);
+ ExecEndNode(epq->plan, NULL);
ExecDropTupleTable(epqstate->es_tupleTable, true);
epqstate->es_tupleTable = NULL;
if (epqstate->es_evTuple[epq->rti - 1] != NULL)
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.26 2001/03/22 06:16:12 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.27 2001/09/18 01:59:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "executor/executor.h"
+#include "executor/instrument.h"
#include "executor/nodeAgg.h"
#include "executor/nodeAppend.h"
#include "executor/nodeGroup.h"
if (node == NULL)
return FALSE;
+ /* Set up instrumentation for this node if the parent has it */
+ if (!node->instrument && parent && parent->instrument)
+ node->instrument = InstrAlloc();
+
foreach(subp, node->initPlan)
{
result = ExecInitSubPlan((SubPlan *) lfirst(subp), estate, node);
switch (nodeTag(node))
{
-
/*
* control nodes
*/
elog(ERROR, "ExecInitNode: node type %d unsupported",
(int) nodeTag(node));
result = FALSE;
+ break;
}
if (result != FALSE)
if (node->chgParam != NULL) /* something changed */
ExecReScan(node, NULL, parent); /* let ReScan handle this */
+ if (node->instrument)
+ InstrStartNode(node->instrument);
+
switch (nodeTag(node))
{
-
- /*
- * control nodes
- */
+ /*
+ * control nodes
+ */
case T_Result:
result = ExecResult((Result *) node);
break;
elog(ERROR, "ExecProcNode: node type %d unsupported",
(int) nodeTag(node));
result = NULL;
+ break;
}
+ if (node->instrument)
+ InstrStopNode(node->instrument, !TupIsNull(result));
+
return result;
}
switch (nodeTag(node))
{
-
/*
* control nodes
*/
switch (nodeTag(node))
{
-
- /*
- * control nodes
- */
+ /*
+ * control nodes
+ */
case T_Result:
ExecEndResult((Result *) node);
break;
(int) nodeTag(node));
break;
}
+
+ if (node->instrument)
+ InstrEndLoop(node->instrument);
}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * instrument.c
+ * functions for instrumentation of plan execution
+ *
+ *
+ * Copyright (c) 2001, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/instrument.c,v 1.1 2001/09/18 01:59:06 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <unistd.h>
+
+#include "executor/instrument.h"
+
+
+/* Allocate new instrumentation structure */
+Instrumentation *
+InstrAlloc(void)
+{
+ Instrumentation *instr = palloc( sizeof(Instrumentation) );
+
+ memset( instr, 0, sizeof(Instrumentation) );
+
+ return instr;
+}
+
+/* Entry to a plan node */
+void
+InstrStartNode(Instrumentation *instr)
+{
+ if (!instr)
+ return;
+
+ if (instr->starttime.tv_sec != 0 || instr->starttime.tv_usec != 0)
+ elog(DEBUG, "InstrStartTimer called twice in a row");
+ else
+ gettimeofday(&instr->starttime, NULL);
+}
+
+/* Exit from a plan node */
+void
+InstrStopNode(Instrumentation *instr, bool returnedTuple)
+{
+ struct timeval endtime;
+
+ if (!instr)
+ return;
+
+ if (instr->starttime.tv_sec == 0 && instr->starttime.tv_usec == 0)
+ {
+ elog(DEBUG, "InstrStopNode without start");
+ return;
+ }
+
+ gettimeofday(&endtime, NULL);
+
+ instr->counter.tv_sec += endtime.tv_sec - instr->starttime.tv_sec;
+ instr->counter.tv_usec += endtime.tv_usec - instr->starttime.tv_usec;
+
+ /* Normalize after each add to avoid overflow/underflow of tv_usec */
+ while (instr->counter.tv_usec < 0)
+ {
+ instr->counter.tv_usec += 1000000;
+ instr->counter.tv_sec--;
+ }
+ while (instr->counter.tv_usec >= 1000000)
+ {
+ instr->counter.tv_usec -= 1000000;
+ instr->counter.tv_sec++;
+ }
+
+ instr->starttime.tv_sec = 0;
+ instr->starttime.tv_usec = 0;
+
+ /* Is this the first tuple of this cycle? */
+ if (!instr->running)
+ {
+ instr->running = true;
+ instr->firsttuple = (double) instr->counter.tv_sec +
+ (double) instr->counter.tv_usec / 1000000.0;
+ }
+
+ if (returnedTuple)
+ instr->tuplecount += 1;
+}
+
+/* Finish a run cycle for a plan node */
+void
+InstrEndLoop(Instrumentation *instr)
+{
+ double totaltime;
+
+ if (!instr)
+ return;
+
+ /* Skip if nothing has happened, or already shut down */
+ if (!instr->running)
+ return;
+
+ /* Accumulate statistics */
+ totaltime = (double) instr->counter.tv_sec +
+ (double) instr->counter.tv_usec / 1000000.0;
+
+ instr->startup += instr->firsttuple;
+ instr->total += totaltime;
+ instr->ntuples += instr->tuplecount;
+ instr->nloops += 1;
+
+ /* Reset for next cycle (if any) */
+ instr->running = false;
+ instr->starttime.tv_sec = 0;
+ instr->starttime.tv_usec = 0;
+ instr->counter.tv_sec = 0;
+ instr->counter.tv_usec = 0;
+ instr->firsttuple = 0;
+ instr->tuplecount = 0;
+}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.30 2001/03/22 03:59:29 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.31 2001/09/18 01:59:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
Assert(pvar == NIL);
- ExecReScan(plan, (ExprContext *) NULL, plan);
+ ExecReScan(plan, NULL, NULL);
/*
* For all sublink types except EXPR_SUBLINK, the result is boolean as
result = BoolGetDatum(subLinkType == ALL_SUBLINK);
*isNull = false;
- for (slot = ExecProcNode(plan, plan);
+ for (slot = ExecProcNode(plan, NULL);
!TupIsNull(slot);
- slot = ExecProcNode(plan, plan))
+ slot = ExecProcNode(plan, NULL))
{
HeapTuple tup = slot->val;
TupleDesc tdesc = slot->ttc_tupleDescriptor;
node->needShutdown = false;
node->curTuple = NULL;
- if (!ExecInitNode(node->plan, sp_estate, NULL))
+ if (!ExecInitNode(node->plan, sp_estate, parent))
return false;
node->needShutdown = true; /* now we need to shutdown the subplan */
elog(ERROR, "ExecSetParamPlan: ANY/ALL subselect unsupported");
if (plan->chgParam != NULL)
- ExecReScan(plan, (ExprContext *) NULL, plan);
+ ExecReScan(plan, NULL, NULL);
- for (slot = ExecProcNode(plan, plan);
+ for (slot = ExecProcNode(plan, NULL);
!TupIsNull(slot);
- slot = ExecProcNode(plan, plan))
+ slot = ExecProcNode(plan, NULL))
{
HeapTuple tup = slot->val;
TupleDesc tdesc = slot->ttc_tupleDescriptor;
if (plan->extParam == NULL) /* un-correlated ... */
{
- ExecEndNode(plan, plan);
+ ExecEndNode(plan, NULL);
node->needShutdown = false;
}
{
if (node->needShutdown)
{
- ExecEndNode(node->plan, node->plan);
+ ExecEndNode(node->plan, NULL);
node->needShutdown = false;
}
if (node->curTuple)
/*
* Don't actual re-scan: ExecSetParamPlan does re-scan if
- * node->plan->chgParam is not NULL... ExecReScan (plan, NULL, plan);
+ * node->plan->chgParam is not NULL... ExecReScan (plan, NULL, NULL);
*/
/*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.9 2001/05/27 20:42:20 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.10 2001/09/18 01:59:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
subquerystate->sss_SubEState->es_direction = direction;
- slot = ExecProcNode(node->subplan, node->subplan);
+ slot = ExecProcNode(node->subplan, (Plan *) node);
subquerystate->csstate.css_ScanTupleSlot = slot;
ExecCreateTupleTable(ExecCountSlotsNode(node->subplan) + 10);
sp_estate->es_snapshot = estate->es_snapshot;
- if (!ExecInitNode(node->subplan, sp_estate, NULL))
+ if (!ExecInitNode(node->subplan, sp_estate, (Plan *) node))
return false;
subquerystate->csstate.css_ScanTupleSlot = NULL;
/*
* close down subquery
*/
- ExecEndNode(node->subplan, node->subplan);
+ ExecEndNode(node->subplan, (Plan *) node);
/*
* clean up subquery's tuple table
* first ExecProcNode.
*/
if (node->subplan->chgParam == NULL)
- ExecReScan(node->subplan, NULL, node->subplan);
+ ExecReScan(node->subplan, NULL, (Plan *) node);
subquerystate->csstate.css_ScanTupleSlot = NULL;
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.155 2001/08/26 16:55:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.156 2001/09/18 01:59:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Node_Copy(from, newnode, query);
newnode->verbose = from->verbose;
+ newnode->analyze = from->analyze;
return newnode;
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.103 2001/08/26 16:55:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.104 2001/09/18 01:59:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return false;
if (a->verbose != b->verbose)
return false;
+ if (a->analyze != b->analyze)
+ return false;
return true;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.250 2001/09/06 04:57:28 ishii Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.251 2001/09/18 01:59:06 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
*
* QUERY:
* EXPLAIN query
+ * EXPLAIN ANALYZE query
*
*****************************************************************************/
{
ExplainStmt *n = makeNode(ExplainStmt);
n->verbose = $2;
+ n->analyze = FALSE;
n->query = (Query*)$3;
$$ = (Node *)n;
}
+ | EXPLAIN analyze_keyword opt_verbose OptimizableStmt
+ {
+ ExplainStmt *n = makeNode(ExplainStmt);
+ n->verbose = $3;
+ n->analyze = TRUE;
+ n->query = (Query*)$4;
+ $$ = (Node *)n;
+ }
;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.117 2001/09/08 01:10:20 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.118 2001/09/18 01:59:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
set_ps_display(commandTag = "EXPLAIN");
- ExplainQuery(stmt->query, stmt->verbose, dest);
+ ExplainQuery(stmt->query, stmt->verbose, stmt->analyze, dest);
}
break;
* 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.11 2001/01/24 19:43:23 momjian Exp $
+ * $Id: explain.h,v 1.12 2001/09/18 01:59:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "nodes/parsenodes.h"
#include "tcop/dest.h"
-extern void ExplainQuery(Query *query, bool verbose, CommandDest dest);
+extern void ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest);
#endif /* EXPLAIN_H */
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * instrument.h
+ * definitions for run-time statistics collection
+ *
+ *
+ * Copyright (c) 2001, PostgreSQL Global Development Group
+ *
+ * $Id: instrument.h,v 1.1 2001/09/18 01:59:06 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef INSTRUMENT_H
+#define INSTRUMENT_H
+
+#include <sys/time.h>
+
+
+typedef struct Instrumentation
+{
+ /* Info about current plan cycle: */
+ bool running; /* TRUE if we've completed first tuple */
+ struct timeval starttime; /* Start time of current iteration of node */
+ struct timeval counter; /* Accumulates runtime for this node */
+ double firsttuple; /* Time for first tuple of this cycle */
+ double tuplecount; /* Tuples so far this cycle */
+ /* Accumulated statistics across all completed cycles: */
+ double startup; /* Total startup time (in seconds) */
+ double total; /* Total total time (in seconds) */
+ double ntuples; /* Total tuples produced */
+ double nloops; /* # of run cycles for this node */
+} Instrumentation;
+
+extern Instrumentation *InstrAlloc(void);
+extern void InstrStartNode(Instrumentation *instr);
+extern void InstrStopNode(Instrumentation *instr, bool returnedTuple);
+extern void InstrEndLoop(Instrumentation *instr);
+
+#endif /* INSTRUMENT_H */
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parsenodes.h,v 1.143 2001/08/26 16:56:02 tgl Exp $
+ * $Id: parsenodes.h,v 1.144 2001/09/18 01:59:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
NodeTag type;
Query *query; /* the query */
bool verbose; /* print plan info */
+ bool analyze; /* get statistics by executing plan */
} ExplainStmt;
/* ----------------------
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: plannodes.h,v 1.49 2001/03/22 04:00:52 momjian Exp $
+ * $Id: plannodes.h,v 1.50 2001/09/18 01:59:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
double plan_rows; /* number of rows plan is expected to emit */
int plan_width; /* average row width in bytes */
+ /*
+ * execution state data. Having Plan point to this, rather than the
+ * other way round, is 100% bogus.
+ */
EState *state; /* at execution time, state's of
* individual nodes point to one EState
* for the whole top-level plan */
+
+ struct Instrumentation *instrument; /* Optional runtime stats for this
+ * plan node */
+
+ /*
+ * Common structural data for all Plan types. XXX chgParam is runtime
+ * data and should be in the EState, not here.
+ */
List *targetlist;
List *qual; /* implicitly-ANDed qual conditions */
struct Plan *lefttree;