mirror of
https://github.com/postgres/postgres.git
synced 2025-06-05 23:56:58 +03:00
Extend EXPLAIN to allow generic options to be specified.
The original syntax made it difficult to add options without making them into reserved words. This change parenthesizes the options to avoid that problem, and makes provision for an explicit (and perhaps non-Boolean) value for each option. The original syntax is still supported, but only for the two original options ANALYZE and VERBOSE. As a test case, add a COSTS option that can suppress the planner cost estimates. This may be useful for including EXPLAIN output in the regression tests, which are otherwise unable to cope with cross-platform variations in cost estimates. Robert Haas
This commit is contained in:
parent
a07e5acebb
commit
d4382c4ae7
@ -6,7 +6,7 @@
|
|||||||
* Copyright (c) 2008-2009, PostgreSQL Global Development Group
|
* Copyright (c) 2008-2009, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/contrib/auto_explain/auto_explain.c,v 1.5 2009/06/11 14:48:50 momjian Exp $
|
* $PostgreSQL: pgsql/contrib/auto_explain/auto_explain.c,v 1.6 2009/07/26 23:34:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -196,16 +196,17 @@ explain_ExecutorEnd(QueryDesc *queryDesc)
|
|||||||
msec = queryDesc->totaltime->total * 1000.0;
|
msec = queryDesc->totaltime->total * 1000.0;
|
||||||
if (msec >= auto_explain_log_min_duration)
|
if (msec >= auto_explain_log_min_duration)
|
||||||
{
|
{
|
||||||
StringInfoData buf;
|
ExplainState es;
|
||||||
|
|
||||||
initStringInfo(&buf);
|
ExplainInitState(&es);
|
||||||
ExplainPrintPlan(&buf, queryDesc,
|
es.analyze = (queryDesc->doInstrument && auto_explain_log_analyze);
|
||||||
queryDesc->doInstrument && auto_explain_log_analyze,
|
es.verbose = auto_explain_log_verbose;
|
||||||
auto_explain_log_verbose);
|
|
||||||
|
ExplainPrintPlan(&es, queryDesc);
|
||||||
|
|
||||||
/* Remove last line break */
|
/* Remove last line break */
|
||||||
if (buf.len > 0 && buf.data[buf.len - 1] == '\n')
|
if (es.str->len > 0 && es.str->data[es.str->len - 1] == '\n')
|
||||||
buf.data[--buf.len] = '\0';
|
es.str->data[--es.str->len] = '\0';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: we rely on the existing logging of context or
|
* Note: we rely on the existing logging of context or
|
||||||
@ -215,9 +216,9 @@ explain_ExecutorEnd(QueryDesc *queryDesc)
|
|||||||
*/
|
*/
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("duration: %.3f ms plan:\n%s",
|
(errmsg("duration: %.3f ms plan:\n%s",
|
||||||
msec, buf.data)));
|
msec, es.str->data)));
|
||||||
|
|
||||||
pfree(buf.data);
|
pfree(es.str->data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$PostgreSQL: pgsql/doc/src/sgml/ref/explain.sgml,v 1.44 2008/11/14 10:22:47 petere Exp $
|
$PostgreSQL: pgsql/doc/src/sgml/ref/explain.sgml,v 1.45 2009/07/26 23:34:17 tgl Exp $
|
||||||
PostgreSQL documentation
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -31,6 +31,7 @@ PostgreSQL documentation
|
|||||||
|
|
||||||
<refsynopsisdiv>
|
<refsynopsisdiv>
|
||||||
<synopsis>
|
<synopsis>
|
||||||
|
EXPLAIN [ ( { ANALYZE <replaceable class="parameter">boolean</replaceable> | VERBOSE <replaceable class="parameter">boolean</replaceable> | COSTS <replaceable class="parameter">boolean</replaceable> } [, ...] ) ] <replaceable class="parameter">statement</replaceable>
|
||||||
EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="parameter">statement</replaceable>
|
EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="parameter">statement</replaceable>
|
||||||
</synopsis>
|
</synopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
@ -89,6 +90,14 @@ ROLLBACK;
|
|||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
</important>
|
</important>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Only the <literal>ANALYZE</literal> and <literal>VERBOSE</literal> options
|
||||||
|
can be specified, and only in that order, without surrounding the option
|
||||||
|
list in parentheses. Prior to <productname>PostgreSQL</productname> 8.5,
|
||||||
|
the unparenthesized syntax was the only one supported. It is expected that
|
||||||
|
all new options will be supported only in the parenthesized syntax.
|
||||||
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
@ -99,7 +108,8 @@ ROLLBACK;
|
|||||||
<term><literal>ANALYZE</literal></term>
|
<term><literal>ANALYZE</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Carry out the command and show the actual run times.
|
Carry out the command and show the actual run times. This
|
||||||
|
parameter defaults to <command>FALSE</command>.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -108,7 +118,33 @@ ROLLBACK;
|
|||||||
<term><literal>VERBOSE</literal></term>
|
<term><literal>VERBOSE</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Include the output column list for each node in the plan tree.
|
Include the output column list for each node in the plan tree. This
|
||||||
|
parameter defaults to <command>FALSE</command>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>COSTS</literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Include information on the estimated startup and total cost of each
|
||||||
|
plan node, as well as the estimated number of rows and the estimated
|
||||||
|
width of each row. This parameter defaults to <command>TRUE</command>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><replaceable class="parameter">boolean</replaceable></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Specifies whether the selected option should be turned on or off.
|
||||||
|
You can write <literal>TRUE</literal>, <literal>ON</>, or
|
||||||
|
<literal>1</literal> to enable the option, and <literal>FALSE</literal>,
|
||||||
|
<literal>OFF</>, or <literal>0</literal> to disable it. The
|
||||||
|
<replaceable class="parameter">boolean</replaceable> value can also
|
||||||
|
be omitted, in which case <literal>TRUE</literal> is assumed.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -149,14 +185,6 @@ ROLLBACK;
|
|||||||
might be chosen.
|
might be chosen.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
|
||||||
Genetic query optimization (<acronym>GEQO</acronym>) randomly tests
|
|
||||||
execution plans. Therefore, when the number of join relations
|
|
||||||
exceeds <xref linkend="guc-geqo-threshold"> causing genetic query
|
|
||||||
optimization to be used, the execution plan is likely to change
|
|
||||||
each time the statement is executed.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
In order to measure the run-time cost of each node in the execution
|
In order to measure the run-time cost of each node in the execution
|
||||||
plan, the current implementation of <command>EXPLAIN
|
plan, the current implementation of <command>EXPLAIN
|
||||||
@ -201,6 +229,20 @@ EXPLAIN SELECT * FROM foo WHERE i = 4;
|
|||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Here is the same plan with costs suppressed:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
EXPLAIN (COSTS FALSE) SELECT * FROM foo WHERE i = 4;
|
||||||
|
|
||||||
|
QUERY PLAN
|
||||||
|
----------------------------
|
||||||
|
Index Scan using fi on foo
|
||||||
|
Index Cond: (i = 4)
|
||||||
|
(2 rows)
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Here is an example of a query plan for a query using an aggregate
|
Here is an example of a query plan for a query using an aggregate
|
||||||
function:
|
function:
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/define.c,v 1.104 2009/04/04 21:12:31 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/define.c,v 1.105 2009/07/26 23:34:17 tgl Exp $
|
||||||
*
|
*
|
||||||
* DESCRIPTION
|
* DESCRIPTION
|
||||||
* The "DefineFoo" routines take the parse tree and pick out the
|
* The "DefineFoo" routines take the parse tree and pick out the
|
||||||
@ -133,7 +133,7 @@ defGetBoolean(DefElem *def)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allow 0, 1, "true", "false"
|
* Allow 0, 1, "true", "false", "on", "off"
|
||||||
*/
|
*/
|
||||||
switch (nodeTag(def->arg))
|
switch (nodeTag(def->arg))
|
||||||
{
|
{
|
||||||
@ -153,11 +153,18 @@ defGetBoolean(DefElem *def)
|
|||||||
{
|
{
|
||||||
char *sval = defGetString(def);
|
char *sval = defGetString(def);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The set of strings accepted here should match up with
|
||||||
|
* the grammar's opt_boolean production.
|
||||||
|
*/
|
||||||
if (pg_strcasecmp(sval, "true") == 0)
|
if (pg_strcasecmp(sval, "true") == 0)
|
||||||
return true;
|
return true;
|
||||||
if (pg_strcasecmp(sval, "false") == 0)
|
if (pg_strcasecmp(sval, "false") == 0)
|
||||||
return false;
|
return false;
|
||||||
|
if (pg_strcasecmp(sval, "on") == 0)
|
||||||
|
return true;
|
||||||
|
if (pg_strcasecmp(sval, "off") == 0)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994-5, Regents of the University of California
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.187 2009/07/24 21:08:42 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.188 2009/07/26 23:34:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -16,6 +16,7 @@
|
|||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/pg_constraint.h"
|
#include "catalog/pg_constraint.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
|
#include "commands/defrem.h"
|
||||||
#include "commands/explain.h"
|
#include "commands/explain.h"
|
||||||
#include "commands/prepare.h"
|
#include "commands/prepare.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
@ -40,20 +41,8 @@ ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL;
|
|||||||
explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
|
explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
|
||||||
|
|
||||||
|
|
||||||
typedef struct ExplainState
|
static void ExplainOneQuery(Query *query, ExplainState *es,
|
||||||
{
|
const char *queryString, ParamListInfo params);
|
||||||
StringInfo str; /* output buffer */
|
|
||||||
/* options */
|
|
||||||
bool printTList; /* print plan targetlists */
|
|
||||||
bool printAnalyze; /* print actual times */
|
|
||||||
/* other states */
|
|
||||||
PlannedStmt *pstmt; /* top of plan */
|
|
||||||
List *rtable; /* range table */
|
|
||||||
} ExplainState;
|
|
||||||
|
|
||||||
static void ExplainOneQuery(Query *query, ExplainStmt *stmt,
|
|
||||||
const char *queryString,
|
|
||||||
ParamListInfo params, TupOutputState *tstate);
|
|
||||||
static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
|
static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
|
||||||
StringInfo buf);
|
StringInfo buf);
|
||||||
static double elapsed_time(instr_time *starttime);
|
static double elapsed_time(instr_time *starttime);
|
||||||
@ -84,11 +73,33 @@ void
|
|||||||
ExplainQuery(ExplainStmt *stmt, const char *queryString,
|
ExplainQuery(ExplainStmt *stmt, const char *queryString,
|
||||||
ParamListInfo params, DestReceiver *dest)
|
ParamListInfo params, DestReceiver *dest)
|
||||||
{
|
{
|
||||||
|
ExplainState es;
|
||||||
Oid *param_types;
|
Oid *param_types;
|
||||||
int num_params;
|
int num_params;
|
||||||
TupOutputState *tstate;
|
TupOutputState *tstate;
|
||||||
List *rewritten;
|
List *rewritten;
|
||||||
ListCell *l;
|
ListCell *lc;
|
||||||
|
|
||||||
|
/* Initialize ExplainState. */
|
||||||
|
ExplainInitState(&es);
|
||||||
|
|
||||||
|
/* Parse options list. */
|
||||||
|
foreach(lc, stmt->options)
|
||||||
|
{
|
||||||
|
DefElem *opt = (DefElem *) lfirst(lc);
|
||||||
|
|
||||||
|
if (strcmp(opt->defname, "analyze") == 0)
|
||||||
|
es.analyze = defGetBoolean(opt);
|
||||||
|
else if (strcmp(opt->defname, "verbose") == 0)
|
||||||
|
es.verbose = defGetBoolean(opt);
|
||||||
|
else if (strcmp(opt->defname, "costs") == 0)
|
||||||
|
es.costs = defGetBoolean(opt);
|
||||||
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("unrecognized EXPLAIN option \"%s\"",
|
||||||
|
opt->defname)));
|
||||||
|
}
|
||||||
|
|
||||||
/* Convert parameter type data to the form parser wants */
|
/* Convert parameter type data to the form parser wants */
|
||||||
getParamListTypes(params, ¶m_types, &num_params);
|
getParamListTypes(params, ¶m_types, &num_params);
|
||||||
@ -106,28 +117,44 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
|
|||||||
rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query),
|
rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query),
|
||||||
queryString, param_types, num_params);
|
queryString, param_types, num_params);
|
||||||
|
|
||||||
/* prepare for projection of tuples */
|
|
||||||
tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt));
|
|
||||||
|
|
||||||
if (rewritten == NIL)
|
if (rewritten == NIL)
|
||||||
{
|
{
|
||||||
/* In the case of an INSTEAD NOTHING, tell at least that */
|
/* In the case of an INSTEAD NOTHING, tell at least that */
|
||||||
do_text_output_oneline(tstate, "Query rewrites to nothing");
|
appendStringInfoString(es.str, "Query rewrites to nothing\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
/* Explain every plan */
|
/* Explain every plan */
|
||||||
foreach(l, rewritten)
|
foreach(l, rewritten)
|
||||||
{
|
{
|
||||||
ExplainOneQuery((Query *) lfirst(l), stmt,
|
ExplainOneQuery((Query *) lfirst(l), &es, queryString, params);
|
||||||
queryString, params, tstate);
|
|
||||||
/* put a blank line between plans */
|
/* put a blank line between plans */
|
||||||
if (lnext(l) != NULL)
|
if (lnext(l) != NULL)
|
||||||
do_text_output_oneline(tstate, "");
|
appendStringInfoChar(es.str, '\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* output tuples */
|
||||||
|
tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt));
|
||||||
|
do_text_output_multiline(tstate, es.str->data);
|
||||||
end_tup_output(tstate);
|
end_tup_output(tstate);
|
||||||
|
|
||||||
|
pfree(es.str->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize ExplainState.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ExplainInitState(ExplainState *es)
|
||||||
|
{
|
||||||
|
/* Set default options. */
|
||||||
|
memset(es, 0, sizeof(ExplainState));
|
||||||
|
es->costs = true;
|
||||||
|
/* Prepare output buffer. */
|
||||||
|
es->str = makeStringInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -151,20 +178,19 @@ ExplainResultDesc(ExplainStmt *stmt)
|
|||||||
* print out the execution plan for one Query
|
* print out the execution plan for one Query
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ExplainOneQuery(Query *query, ExplainStmt *stmt, const char *queryString,
|
ExplainOneQuery(Query *query, ExplainState *es,
|
||||||
ParamListInfo params, TupOutputState *tstate)
|
const char *queryString, ParamListInfo params)
|
||||||
{
|
{
|
||||||
/* planner will not cope with utility statements */
|
/* planner will not cope with utility statements */
|
||||||
if (query->commandType == CMD_UTILITY)
|
if (query->commandType == CMD_UTILITY)
|
||||||
{
|
{
|
||||||
ExplainOneUtility(query->utilityStmt, stmt,
|
ExplainOneUtility(query->utilityStmt, es, queryString, params);
|
||||||
queryString, params, tstate);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if an advisor plugin is present, let it manage things */
|
/* if an advisor plugin is present, let it manage things */
|
||||||
if (ExplainOneQuery_hook)
|
if (ExplainOneQuery_hook)
|
||||||
(*ExplainOneQuery_hook) (query, stmt, queryString, params, tstate);
|
(*ExplainOneQuery_hook) (query, es, queryString, params);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PlannedStmt *plan;
|
PlannedStmt *plan;
|
||||||
@ -173,7 +199,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, const char *queryString,
|
|||||||
plan = pg_plan_query(query, 0, params);
|
plan = pg_plan_query(query, 0, params);
|
||||||
|
|
||||||
/* run it (if needed) and produce output */
|
/* run it (if needed) and produce output */
|
||||||
ExplainOnePlan(plan, stmt, queryString, params, tstate);
|
ExplainOnePlan(plan, es, queryString, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,21 +213,20 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, const char *queryString,
|
|||||||
* EXPLAIN EXECUTE case
|
* EXPLAIN EXECUTE case
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt,
|
ExplainOneUtility(Node *utilityStmt, ExplainState *es,
|
||||||
const char *queryString, ParamListInfo params,
|
const char *queryString, ParamListInfo params)
|
||||||
TupOutputState *tstate)
|
|
||||||
{
|
{
|
||||||
if (utilityStmt == NULL)
|
if (utilityStmt == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (IsA(utilityStmt, ExecuteStmt))
|
if (IsA(utilityStmt, ExecuteStmt))
|
||||||
ExplainExecuteQuery((ExecuteStmt *) utilityStmt, stmt,
|
ExplainExecuteQuery((ExecuteStmt *) utilityStmt, es,
|
||||||
queryString, params, tstate);
|
queryString, params);
|
||||||
else if (IsA(utilityStmt, NotifyStmt))
|
else if (IsA(utilityStmt, NotifyStmt))
|
||||||
do_text_output_oneline(tstate, "NOTIFY");
|
appendStringInfoString(es->str, "NOTIFY\n");
|
||||||
else
|
else
|
||||||
do_text_output_oneline(tstate,
|
appendStringInfoString(es->str,
|
||||||
"Utility statements have no plan structure");
|
"Utility statements have no plan structure\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -219,14 +244,12 @@ ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt,
|
|||||||
* to call it.
|
* to call it.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
|
ExplainOnePlan(PlannedStmt *plannedstmt, ExplainState *es,
|
||||||
const char *queryString, ParamListInfo params,
|
const char *queryString, ParamListInfo params)
|
||||||
TupOutputState *tstate)
|
|
||||||
{
|
{
|
||||||
QueryDesc *queryDesc;
|
QueryDesc *queryDesc;
|
||||||
instr_time starttime;
|
instr_time starttime;
|
||||||
double totaltime = 0;
|
double totaltime = 0;
|
||||||
StringInfoData buf;
|
|
||||||
int eflags;
|
int eflags;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -238,17 +261,16 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
|
|||||||
/* Create a QueryDesc requesting no output */
|
/* Create a QueryDesc requesting no output */
|
||||||
queryDesc = CreateQueryDesc(plannedstmt, queryString,
|
queryDesc = CreateQueryDesc(plannedstmt, queryString,
|
||||||
GetActiveSnapshot(), InvalidSnapshot,
|
GetActiveSnapshot(), InvalidSnapshot,
|
||||||
None_Receiver, params,
|
None_Receiver, params, es->analyze);
|
||||||
stmt->analyze);
|
|
||||||
|
|
||||||
INSTR_TIME_SET_CURRENT(starttime);
|
INSTR_TIME_SET_CURRENT(starttime);
|
||||||
|
|
||||||
/* If analyzing, we need to cope with queued triggers */
|
/* If analyzing, we need to cope with queued triggers */
|
||||||
if (stmt->analyze)
|
if (es->analyze)
|
||||||
AfterTriggerBeginQuery();
|
AfterTriggerBeginQuery();
|
||||||
|
|
||||||
/* Select execution options */
|
/* Select execution options */
|
||||||
if (stmt->analyze)
|
if (es->analyze)
|
||||||
eflags = 0; /* default run-to-completion flags */
|
eflags = 0; /* default run-to-completion flags */
|
||||||
else
|
else
|
||||||
eflags = EXEC_FLAG_EXPLAIN_ONLY;
|
eflags = EXEC_FLAG_EXPLAIN_ONLY;
|
||||||
@ -257,7 +279,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
|
|||||||
ExecutorStart(queryDesc, eflags);
|
ExecutorStart(queryDesc, eflags);
|
||||||
|
|
||||||
/* Execute the plan for statistics if asked for */
|
/* Execute the plan for statistics if asked for */
|
||||||
if (stmt->analyze)
|
if (es->analyze)
|
||||||
{
|
{
|
||||||
/* run the plan */
|
/* run the plan */
|
||||||
ExecutorRun(queryDesc, ForwardScanDirection, 0L);
|
ExecutorRun(queryDesc, ForwardScanDirection, 0L);
|
||||||
@ -267,15 +289,14 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Create textual dump of plan tree */
|
/* Create textual dump of plan tree */
|
||||||
initStringInfo(&buf);
|
ExplainPrintPlan(es, queryDesc);
|
||||||
ExplainPrintPlan(&buf, queryDesc, stmt->analyze, stmt->verbose);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we ran the command, run any AFTER triggers it queued. (Note this
|
* If we ran the command, run any AFTER triggers it queued. (Note this
|
||||||
* will not include DEFERRED triggers; since those don't run until end of
|
* will not include DEFERRED triggers; since those don't run until end of
|
||||||
* transaction, we can't measure them.) Include into total runtime.
|
* transaction, we can't measure them.) Include into total runtime.
|
||||||
*/
|
*/
|
||||||
if (stmt->analyze)
|
if (es->analyze)
|
||||||
{
|
{
|
||||||
INSTR_TIME_SET_CURRENT(starttime);
|
INSTR_TIME_SET_CURRENT(starttime);
|
||||||
AfterTriggerEndQuery(queryDesc->estate);
|
AfterTriggerEndQuery(queryDesc->estate);
|
||||||
@ -283,7 +304,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Print info about runtime of triggers */
|
/* Print info about runtime of triggers */
|
||||||
if (stmt->analyze)
|
if (es->analyze)
|
||||||
{
|
{
|
||||||
ResultRelInfo *rInfo;
|
ResultRelInfo *rInfo;
|
||||||
bool show_relname;
|
bool show_relname;
|
||||||
@ -295,12 +316,12 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
|
|||||||
show_relname = (numrels > 1 || targrels != NIL);
|
show_relname = (numrels > 1 || targrels != NIL);
|
||||||
rInfo = queryDesc->estate->es_result_relations;
|
rInfo = queryDesc->estate->es_result_relations;
|
||||||
for (nr = 0; nr < numrels; rInfo++, nr++)
|
for (nr = 0; nr < numrels; rInfo++, nr++)
|
||||||
report_triggers(rInfo, show_relname, &buf);
|
report_triggers(rInfo, show_relname, es->str);
|
||||||
|
|
||||||
foreach(l, targrels)
|
foreach(l, targrels)
|
||||||
{
|
{
|
||||||
rInfo = (ResultRelInfo *) lfirst(l);
|
rInfo = (ResultRelInfo *) lfirst(l);
|
||||||
report_triggers(rInfo, show_relname, &buf);
|
report_triggers(rInfo, show_relname, es->str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,45 +338,34 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
|
|||||||
PopActiveSnapshot();
|
PopActiveSnapshot();
|
||||||
|
|
||||||
/* We need a CCI just in case query expanded to multiple plans */
|
/* We need a CCI just in case query expanded to multiple plans */
|
||||||
if (stmt->analyze)
|
if (es->analyze)
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
totaltime += elapsed_time(&starttime);
|
totaltime += elapsed_time(&starttime);
|
||||||
|
|
||||||
if (stmt->analyze)
|
if (es->analyze)
|
||||||
appendStringInfo(&buf, "Total runtime: %.3f ms\n",
|
appendStringInfo(es->str, "Total runtime: %.3f ms\n",
|
||||||
1000.0 * totaltime);
|
1000.0 * totaltime);
|
||||||
do_text_output_multiline(tstate, buf.data);
|
|
||||||
|
|
||||||
pfree(buf.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ExplainPrintPlan -
|
* ExplainPrintPlan -
|
||||||
* convert a QueryDesc's plan tree to text and append it to 'str'
|
* convert a QueryDesc's plan tree to text and append it to es->str
|
||||||
*
|
*
|
||||||
* 'analyze' means to include runtime instrumentation results
|
* The caller should have set up the options fields of *es, as well as
|
||||||
* 'verbose' means a verbose printout (currently, it shows targetlists)
|
* initializing the output buffer es->str. Other fields in *es are
|
||||||
|
* initialized here.
|
||||||
*
|
*
|
||||||
* NB: will not work on utility statements
|
* NB: will not work on utility statements
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExplainPrintPlan(StringInfo str, QueryDesc *queryDesc,
|
ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
|
||||||
bool analyze, bool verbose)
|
|
||||||
{
|
{
|
||||||
ExplainState es;
|
|
||||||
|
|
||||||
Assert(queryDesc->plannedstmt != NULL);
|
Assert(queryDesc->plannedstmt != NULL);
|
||||||
|
es->pstmt = queryDesc->plannedstmt;
|
||||||
memset(&es, 0, sizeof(es));
|
es->rtable = queryDesc->plannedstmt->rtable;
|
||||||
es.str = str;
|
|
||||||
es.printTList = verbose;
|
|
||||||
es.printAnalyze = analyze;
|
|
||||||
es.pstmt = queryDesc->plannedstmt;
|
|
||||||
es.rtable = queryDesc->plannedstmt->rtable;
|
|
||||||
|
|
||||||
ExplainNode(queryDesc->plannedstmt->planTree, queryDesc->planstate,
|
ExplainNode(queryDesc->plannedstmt->planTree, queryDesc->planstate,
|
||||||
NULL, 0, &es);
|
NULL, 0, es);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -692,9 +702,10 @@ ExplainNode(Plan *plan, PlanState *planstate,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
appendStringInfo(es->str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
|
if (es->costs)
|
||||||
plan->startup_cost, plan->total_cost,
|
appendStringInfo(es->str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
|
||||||
plan->plan_rows, plan->plan_width);
|
plan->startup_cost, plan->total_cost,
|
||||||
|
plan->plan_rows, plan->plan_width);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We have to forcibly clean up the instrumentation state because we
|
* We have to forcibly clean up the instrumentation state because we
|
||||||
@ -714,12 +725,12 @@ ExplainNode(Plan *plan, PlanState *planstate,
|
|||||||
planstate->instrument->ntuples / nloops,
|
planstate->instrument->ntuples / nloops,
|
||||||
planstate->instrument->nloops);
|
planstate->instrument->nloops);
|
||||||
}
|
}
|
||||||
else if (es->printAnalyze)
|
else if (es->analyze)
|
||||||
appendStringInfoString(es->str, " (never executed)");
|
appendStringInfoString(es->str, " (never executed)");
|
||||||
appendStringInfoChar(es->str, '\n');
|
appendStringInfoChar(es->str, '\n');
|
||||||
|
|
||||||
/* target list */
|
/* target list */
|
||||||
if (es->printTList)
|
if (es->verbose)
|
||||||
show_plan_tlist(plan, indent, es);
|
show_plan_tlist(plan, indent, es);
|
||||||
|
|
||||||
/* quals, sort keys, etc */
|
/* quals, sort keys, etc */
|
||||||
@ -1025,7 +1036,7 @@ static void
|
|||||||
show_sort_info(SortState *sortstate, int indent, ExplainState *es)
|
show_sort_info(SortState *sortstate, int indent, ExplainState *es)
|
||||||
{
|
{
|
||||||
Assert(IsA(sortstate, SortState));
|
Assert(IsA(sortstate, SortState));
|
||||||
if (es->printAnalyze && sortstate->sort_Done &&
|
if (es->analyze && sortstate->sort_Done &&
|
||||||
sortstate->tuplesortstate != NULL)
|
sortstate->tuplesortstate != NULL)
|
||||||
{
|
{
|
||||||
char *sortinfo;
|
char *sortinfo;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
|
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.97 2009/06/11 14:48:56 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.98 2009/07/26 23:34:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/explain.h"
|
|
||||||
#include "commands/prepare.h"
|
#include "commands/prepare.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
@ -641,9 +640,8 @@ DropAllPreparedStatements(void)
|
|||||||
* not the original PREPARE; we get the latter string from the plancache.
|
* not the original PREPARE; we get the latter string from the plancache.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt,
|
ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainState *es,
|
||||||
const char *queryString,
|
const char *queryString, ParamListInfo params)
|
||||||
ParamListInfo params, TupOutputState *tstate)
|
|
||||||
{
|
{
|
||||||
PreparedStatement *entry;
|
PreparedStatement *entry;
|
||||||
const char *query_string;
|
const char *query_string;
|
||||||
@ -707,20 +705,18 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt,
|
|||||||
pstmt->intoClause = execstmt->into;
|
pstmt->intoClause = execstmt->into;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExplainOnePlan(pstmt, stmt, query_string,
|
ExplainOnePlan(pstmt, es, query_string, paramLI);
|
||||||
paramLI, tstate);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ExplainOneUtility((Node *) pstmt, stmt, query_string,
|
ExplainOneUtility((Node *) pstmt, es, query_string, params);
|
||||||
params, tstate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* No need for CommandCounterIncrement, as ExplainOnePlan did it */
|
/* No need for CommandCounterIncrement, as ExplainOnePlan did it */
|
||||||
|
|
||||||
/* put a blank line between plans */
|
/* put a blank line between plans */
|
||||||
if (!is_last_query)
|
if (!is_last_query)
|
||||||
do_text_output_oneline(tstate, "");
|
appendStringInfoChar(es->str, '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (estate)
|
if (estate)
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.434 2009/07/20 02:42:27 adunstan Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.435 2009/07/26 23:34:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -2875,8 +2875,7 @@ _copyExplainStmt(ExplainStmt *from)
|
|||||||
ExplainStmt *newnode = makeNode(ExplainStmt);
|
ExplainStmt *newnode = makeNode(ExplainStmt);
|
||||||
|
|
||||||
COPY_NODE_FIELD(query);
|
COPY_NODE_FIELD(query);
|
||||||
COPY_SCALAR_FIELD(verbose);
|
COPY_NODE_FIELD(options);
|
||||||
COPY_SCALAR_FIELD(analyze);
|
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.357 2009/07/20 02:42:27 adunstan Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.358 2009/07/26 23:34:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1467,8 +1467,7 @@ static bool
|
|||||||
_equalExplainStmt(ExplainStmt *a, ExplainStmt *b)
|
_equalExplainStmt(ExplainStmt *a, ExplainStmt *b)
|
||||||
{
|
{
|
||||||
COMPARE_NODE_FIELD(query);
|
COMPARE_NODE_FIELD(query);
|
||||||
COMPARE_SCALAR_FIELD(verbose);
|
COMPARE_NODE_FIELD(options);
|
||||||
COMPARE_SCALAR_FIELD(analyze);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.672 2009/07/25 00:07:11 adunstan Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.673 2009/07/26 23:34:18 tgl Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -321,7 +321,7 @@ static TypeName *TableFuncTypeName(List *columns);
|
|||||||
%type <list> opt_interval interval_second
|
%type <list> opt_interval interval_second
|
||||||
%type <node> overlay_placing substr_from substr_for
|
%type <node> overlay_placing substr_from substr_for
|
||||||
|
|
||||||
%type <boolean> opt_instead opt_analyze
|
%type <boolean> opt_instead
|
||||||
%type <boolean> index_opt_unique opt_verbose opt_full
|
%type <boolean> index_opt_unique opt_verbose opt_full
|
||||||
%type <boolean> opt_freeze opt_default opt_recheck
|
%type <boolean> opt_freeze opt_default opt_recheck
|
||||||
%type <defelt> opt_binary opt_oids copy_delimiter
|
%type <defelt> opt_binary opt_oids copy_delimiter
|
||||||
@ -368,6 +368,10 @@ static TypeName *TableFuncTypeName(List *columns);
|
|||||||
%type <node> generic_option_arg
|
%type <node> generic_option_arg
|
||||||
%type <defelt> generic_option_elem alter_generic_option_elem
|
%type <defelt> generic_option_elem alter_generic_option_elem
|
||||||
%type <list> generic_option_list alter_generic_option_list
|
%type <list> generic_option_list alter_generic_option_list
|
||||||
|
%type <str> explain_option_name
|
||||||
|
%type <node> explain_option_arg
|
||||||
|
%type <defelt> explain_option_elem
|
||||||
|
%type <list> explain_option_list
|
||||||
|
|
||||||
%type <typnam> Typename SimpleTypename ConstTypename
|
%type <typnam> Typename SimpleTypename ConstTypename
|
||||||
GenericType Numeric opt_float
|
GenericType Numeric opt_float
|
||||||
@ -2710,13 +2714,13 @@ opt_by: BY {}
|
|||||||
;
|
;
|
||||||
|
|
||||||
NumericOnly:
|
NumericOnly:
|
||||||
FCONST { $$ = makeFloat($1); }
|
FCONST { $$ = makeFloat($1); }
|
||||||
| '-' FCONST
|
| '-' FCONST
|
||||||
{
|
{
|
||||||
$$ = makeFloat($2);
|
$$ = makeFloat($2);
|
||||||
doNegateFloat($$);
|
doNegateFloat($$);
|
||||||
}
|
}
|
||||||
| SignedIconst { $$ = makeInteger($1); };
|
| SignedIconst { $$ = makeInteger($1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
@ -6473,16 +6477,41 @@ opt_name_list:
|
|||||||
*
|
*
|
||||||
* QUERY:
|
* QUERY:
|
||||||
* EXPLAIN [ANALYZE] [VERBOSE] query
|
* EXPLAIN [ANALYZE] [VERBOSE] query
|
||||||
|
* EXPLAIN ( options ) query
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
ExplainStmt: EXPLAIN opt_analyze opt_verbose ExplainableStmt
|
ExplainStmt:
|
||||||
|
EXPLAIN ExplainableStmt
|
||||||
|
{
|
||||||
|
ExplainStmt *n = makeNode(ExplainStmt);
|
||||||
|
n->query = $2;
|
||||||
|
n->options = NIL;
|
||||||
|
$$ = (Node *) n;
|
||||||
|
}
|
||||||
|
| EXPLAIN analyze_keyword opt_verbose ExplainableStmt
|
||||||
{
|
{
|
||||||
ExplainStmt *n = makeNode(ExplainStmt);
|
ExplainStmt *n = makeNode(ExplainStmt);
|
||||||
n->analyze = $2;
|
|
||||||
n->verbose = $3;
|
|
||||||
n->query = $4;
|
n->query = $4;
|
||||||
$$ = (Node *)n;
|
n->options = list_make1(makeDefElem("analyze", NULL));
|
||||||
|
if ($3)
|
||||||
|
n->options = lappend(n->options,
|
||||||
|
makeDefElem("verbose", NULL));
|
||||||
|
$$ = (Node *) n;
|
||||||
|
}
|
||||||
|
| EXPLAIN VERBOSE ExplainableStmt
|
||||||
|
{
|
||||||
|
ExplainStmt *n = makeNode(ExplainStmt);
|
||||||
|
n->query = $3;
|
||||||
|
n->options = list_make1(makeDefElem("verbose", NULL));
|
||||||
|
$$ = (Node *) n;
|
||||||
|
}
|
||||||
|
| EXPLAIN '(' explain_option_list ')' ExplainableStmt
|
||||||
|
{
|
||||||
|
ExplainStmt *n = makeNode(ExplainStmt);
|
||||||
|
n->query = $5;
|
||||||
|
n->options = $3;
|
||||||
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -6496,9 +6525,35 @@ ExplainableStmt:
|
|||||||
| ExecuteStmt /* by default all are $$=$1 */
|
| ExecuteStmt /* by default all are $$=$1 */
|
||||||
;
|
;
|
||||||
|
|
||||||
opt_analyze:
|
explain_option_list:
|
||||||
analyze_keyword { $$ = TRUE; }
|
explain_option_elem
|
||||||
| /* EMPTY */ { $$ = FALSE; }
|
{
|
||||||
|
$$ = list_make1($1);
|
||||||
|
}
|
||||||
|
| explain_option_list ',' explain_option_elem
|
||||||
|
{
|
||||||
|
$$ = lappend($1, $3);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
explain_option_elem:
|
||||||
|
explain_option_name explain_option_arg
|
||||||
|
{
|
||||||
|
$$ = makeDefElem($1, $2);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
explain_option_name:
|
||||||
|
ColId { $$ = $1; }
|
||||||
|
| analyze_keyword { $$ = "analyze"; }
|
||||||
|
| VERBOSE { $$ = "verbose"; }
|
||||||
|
;
|
||||||
|
|
||||||
|
explain_option_arg:
|
||||||
|
opt_boolean { $$ = (Node *) makeString($1); }
|
||||||
|
| ColId_or_Sconst { $$ = (Node *) makeString($1); }
|
||||||
|
| NumericOnly { $$ = (Node *) $1; }
|
||||||
|
| /* EMPTY */ { $$ = NULL; }
|
||||||
;
|
;
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.310 2009/07/16 06:33:44 petere Exp $
|
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.311 2009/07/26 23:34:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -2316,10 +2316,20 @@ GetCommandLogLevel(Node *parsetree)
|
|||||||
case T_ExplainStmt:
|
case T_ExplainStmt:
|
||||||
{
|
{
|
||||||
ExplainStmt *stmt = (ExplainStmt *) parsetree;
|
ExplainStmt *stmt = (ExplainStmt *) parsetree;
|
||||||
|
bool analyze = false;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
/* Look through an EXPLAIN ANALYZE to the contained stmt */
|
/* Look through an EXPLAIN ANALYZE to the contained stmt */
|
||||||
if (stmt->analyze)
|
foreach(lc, stmt->options)
|
||||||
|
{
|
||||||
|
DefElem *opt = (DefElem *) lfirst(lc);
|
||||||
|
|
||||||
|
if (strcmp(opt->defname, "analyze") == 0)
|
||||||
|
analyze = defGetBoolean(opt);
|
||||||
|
}
|
||||||
|
if (analyze)
|
||||||
return GetCommandLogLevel(stmt->query);
|
return GetCommandLogLevel(stmt->query);
|
||||||
|
|
||||||
/* Plain EXPLAIN isn't so interesting */
|
/* Plain EXPLAIN isn't so interesting */
|
||||||
lev = LOGSTMT_ALL;
|
lev = LOGSTMT_ALL;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994-5, Regents of the University of California
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/explain.h,v 1.39 2009/06/11 14:49:11 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/commands/explain.h,v 1.40 2009/07/26 23:34:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -15,12 +15,23 @@
|
|||||||
|
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
|
|
||||||
|
typedef struct ExplainState
|
||||||
|
{
|
||||||
|
StringInfo str; /* output buffer */
|
||||||
|
/* options */
|
||||||
|
bool verbose; /* print plan targetlists */
|
||||||
|
bool analyze; /* print actual times */
|
||||||
|
bool costs; /* print costs */
|
||||||
|
/* other states */
|
||||||
|
PlannedStmt *pstmt; /* top of plan */
|
||||||
|
List *rtable; /* range table */
|
||||||
|
} ExplainState;
|
||||||
|
|
||||||
/* Hook for plugins to get control in ExplainOneQuery() */
|
/* Hook for plugins to get control in ExplainOneQuery() */
|
||||||
typedef void (*ExplainOneQuery_hook_type) (Query *query,
|
typedef void (*ExplainOneQuery_hook_type) (Query *query,
|
||||||
ExplainStmt *stmt,
|
ExplainState *es,
|
||||||
const char *queryString,
|
const char *queryString,
|
||||||
ParamListInfo params,
|
ParamListInfo params);
|
||||||
TupOutputState *tstate);
|
|
||||||
extern PGDLLIMPORT ExplainOneQuery_hook_type ExplainOneQuery_hook;
|
extern PGDLLIMPORT ExplainOneQuery_hook_type ExplainOneQuery_hook;
|
||||||
|
|
||||||
/* Hook for plugins to get control in explain_get_index_name() */
|
/* Hook for plugins to get control in explain_get_index_name() */
|
||||||
@ -31,19 +42,16 @@ extern PGDLLIMPORT explain_get_index_name_hook_type explain_get_index_name_hook;
|
|||||||
extern void ExplainQuery(ExplainStmt *stmt, const char *queryString,
|
extern void ExplainQuery(ExplainStmt *stmt, const char *queryString,
|
||||||
ParamListInfo params, DestReceiver *dest);
|
ParamListInfo params, DestReceiver *dest);
|
||||||
|
|
||||||
|
extern void ExplainInitState(ExplainState *es);
|
||||||
|
|
||||||
extern TupleDesc ExplainResultDesc(ExplainStmt *stmt);
|
extern TupleDesc ExplainResultDesc(ExplainStmt *stmt);
|
||||||
|
|
||||||
extern void ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt,
|
extern void ExplainOneUtility(Node *utilityStmt, ExplainState *es,
|
||||||
const char *queryString,
|
const char *queryString, ParamListInfo params);
|
||||||
ParamListInfo params,
|
|
||||||
TupOutputState *tstate);
|
|
||||||
|
|
||||||
extern void ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
|
extern void ExplainOnePlan(PlannedStmt *plannedstmt, ExplainState *es,
|
||||||
const char *queryString,
|
const char *queryString, ParamListInfo params);
|
||||||
ParamListInfo params,
|
|
||||||
TupOutputState *tstate);
|
|
||||||
|
|
||||||
extern void ExplainPrintPlan(StringInfo str, QueryDesc *queryDesc,
|
extern void ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc);
|
||||||
bool analyze, bool verbose);
|
|
||||||
|
|
||||||
#endif /* EXPLAIN_H */
|
#endif /* EXPLAIN_H */
|
||||||
|
@ -6,14 +6,14 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
|
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.30 2009/01/01 17:23:58 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.31 2009/07/26 23:34:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#ifndef PREPARE_H
|
#ifndef PREPARE_H
|
||||||
#define PREPARE_H
|
#define PREPARE_H
|
||||||
|
|
||||||
#include "executor/executor.h"
|
#include "commands/explain.h"
|
||||||
#include "utils/plancache.h"
|
#include "utils/plancache.h"
|
||||||
#include "utils/timestamp.h"
|
#include "utils/timestamp.h"
|
||||||
|
|
||||||
@ -40,9 +40,8 @@ extern void ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
|
|||||||
ParamListInfo params,
|
ParamListInfo params,
|
||||||
DestReceiver *dest, char *completionTag);
|
DestReceiver *dest, char *completionTag);
|
||||||
extern void DeallocateQuery(DeallocateStmt *stmt);
|
extern void DeallocateQuery(DeallocateStmt *stmt);
|
||||||
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt,
|
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainState *es,
|
||||||
const char *queryString,
|
const char *queryString, ParamListInfo params);
|
||||||
ParamListInfo params, TupOutputState *tstate);
|
|
||||||
|
|
||||||
/* Low-level access to stored prepared statements */
|
/* Low-level access to stored prepared statements */
|
||||||
extern void StorePreparedStatement(const char *stmt_name,
|
extern void StorePreparedStatement(const char *stmt_name,
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.397 2009/07/20 02:42:28 adunstan Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.398 2009/07/26 23:34:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -2193,8 +2193,7 @@ typedef struct ExplainStmt
|
|||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
Node *query; /* the query (as a raw parse tree) */
|
Node *query; /* the query (as a raw parse tree) */
|
||||||
bool verbose; /* print plan info */
|
List *options; /* list of DefElem nodes */
|
||||||
bool analyze; /* get statistics by executing plan */
|
|
||||||
} ExplainStmt;
|
} ExplainStmt;
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
|
Loading…
x
Reference in New Issue
Block a user