Commit d4382c4a authored by Tom Lane's avatar Tom Lane

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
parent a07e5ace
......@@ -6,7 +6,7 @@
* Copyright (c) 2008-2009, PostgreSQL Global Development Group
*
* 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;
if (msec >= auto_explain_log_min_duration)
{
StringInfoData buf;
ExplainState es;
initStringInfo(&buf);
ExplainPrintPlan(&buf, queryDesc,
queryDesc->doInstrument && auto_explain_log_analyze,
auto_explain_log_verbose);
ExplainInitState(&es);
es.analyze = (queryDesc->doInstrument && auto_explain_log_analyze);
es.verbose = auto_explain_log_verbose;
ExplainPrintPlan(&es, queryDesc);
/* Remove last line break */
if (buf.len > 0 && buf.data[buf.len - 1] == '\n')
buf.data[--buf.len] = '\0';
if (es.str->len > 0 && es.str->data[es.str->len - 1] == '\n')
es.str->data[--es.str->len] = '\0';
/*
* Note: we rely on the existing logging of context or
......@@ -215,9 +216,9 @@ explain_ExecutorEnd(QueryDesc *queryDesc)
*/
ereport(LOG,
(errmsg("duration: %.3f ms plan:\n%s",
msec, buf.data)));
msec, es.str->data)));
pfree(buf.data);
pfree(es.str->data);
}
}
......
<!--
$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
-->
......@@ -31,6 +31,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<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>
</synopsis>
</refsynopsisdiv>
......@@ -89,6 +90,14 @@ ROLLBACK;
</programlisting>
</para>
</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>
......@@ -99,7 +108,8 @@ ROLLBACK;
<term><literal>ANALYZE</literal></term>
<listitem>
<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>
</listitem>
</varlistentry>
......@@ -108,7 +118,33 @@ ROLLBACK;
<term><literal>VERBOSE</literal></term>
<listitem>
<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>
</listitem>
</varlistentry>
......@@ -149,14 +185,6 @@ ROLLBACK;
might be chosen.
</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>
In order to measure the run-time cost of each node in the execution
plan, the current implementation of <command>EXPLAIN
......@@ -201,6 +229,20 @@ EXPLAIN SELECT * FROM foo WHERE i = 4;
</programlisting>
</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>
Here is an example of a query plan for a query using an aggregate
function:
......
......@@ -9,7 +9,7 @@
*
*
* 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
* The "DefineFoo" routines take the parse tree and pick out the
......@@ -133,7 +133,7 @@ defGetBoolean(DefElem *def)
return true;
/*
* Allow 0, 1, "true", "false"
* Allow 0, 1, "true", "false", "on", "off"
*/
switch (nodeTag(def->arg))
{
......@@ -153,11 +153,18 @@ defGetBoolean(DefElem *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)
return true;
if (pg_strcasecmp(sval, "false") == 0)
return false;
if (pg_strcasecmp(sval, "on") == 0)
return true;
if (pg_strcasecmp(sval, "off") == 0)
return false;
}
break;
}
......
This diff is collapsed.
......@@ -10,7 +10,7 @@
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
*
* 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 "catalog/pg_type.h"
#include "commands/explain.h"
#include "commands/prepare.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
......@@ -641,9 +640,8 @@ DropAllPreparedStatements(void)
* not the original PREPARE; we get the latter string from the plancache.
*/
void
ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt,
const char *queryString,
ParamListInfo params, TupOutputState *tstate)
ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainState *es,
const char *queryString, ParamListInfo params)
{
PreparedStatement *entry;
const char *query_string;
......@@ -707,20 +705,18 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt,
pstmt->intoClause = execstmt->into;
}
ExplainOnePlan(pstmt, stmt, query_string,
paramLI, tstate);
ExplainOnePlan(pstmt, es, query_string, paramLI);
}
else
{
ExplainOneUtility((Node *) pstmt, stmt, query_string,
params, tstate);
ExplainOneUtility((Node *) pstmt, es, query_string, params);
}
/* No need for CommandCounterIncrement, as ExplainOnePlan did it */
/* put a blank line between plans */
if (!is_last_query)
do_text_output_oneline(tstate, "");
appendStringInfoChar(es->str, '\n');
}
if (estate)
......
......@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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);
COPY_NODE_FIELD(query);
COPY_SCALAR_FIELD(verbose);
COPY_SCALAR_FIELD(analyze);
COPY_NODE_FIELD(options);
return newnode;
}
......
......@@ -22,7 +22,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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)
{
COMPARE_NODE_FIELD(query);
COMPARE_SCALAR_FIELD(verbose);
COMPARE_SCALAR_FIELD(analyze);
COMPARE_NODE_FIELD(options);
return true;
}
......
......@@ -11,7 +11,7 @@
*
*
* 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
* AUTHOR DATE MAJOR EVENT
......@@ -321,7 +321,7 @@ static TypeName *TableFuncTypeName(List *columns);
%type <list> opt_interval interval_second
%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> opt_freeze opt_default opt_recheck
%type <defelt> opt_binary opt_oids copy_delimiter
......@@ -368,6 +368,10 @@ static TypeName *TableFuncTypeName(List *columns);
%type <node> generic_option_arg
%type <defelt> generic_option_elem alter_generic_option_elem
%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
GenericType Numeric opt_float
......@@ -2710,13 +2714,13 @@ opt_by: BY {}
;
NumericOnly:
FCONST { $$ = makeFloat($1); }
FCONST { $$ = makeFloat($1); }
| '-' FCONST
{
$$ = makeFloat($2);
doNegateFloat($$);
}
| SignedIconst { $$ = makeInteger($1); };
| SignedIconst { $$ = makeInteger($1); }
;
/*****************************************************************************
......@@ -6473,16 +6477,41 @@ opt_name_list:
*
* 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);
n->analyze = $2;
n->verbose = $3;
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 */
;
opt_analyze:
analyze_keyword { $$ = TRUE; }
| /* EMPTY */ { $$ = FALSE; }
explain_option_list:
explain_option_elem
{
$$ = 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
* $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:
{
ExplainStmt *stmt = (ExplainStmt *) parsetree;
bool analyze = false;
ListCell *lc;
/* 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);
/* Plain EXPLAIN isn't so interesting */
lev = LOGSTMT_ALL;
}
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* 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"
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() */
typedef void (*ExplainOneQuery_hook_type) (Query *query,
ExplainStmt *stmt,
const char *queryString,
ParamListInfo params,
TupOutputState *tstate);
ExplainState *es,
const char *queryString,
ParamListInfo params);
extern PGDLLIMPORT ExplainOneQuery_hook_type ExplainOneQuery_hook;
/* 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,
ParamListInfo params, DestReceiver *dest);
extern void ExplainInitState(ExplainState *es);
extern TupleDesc ExplainResultDesc(ExplainStmt *stmt);
extern void ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt,
const char *queryString,
ParamListInfo params,
TupOutputState *tstate);
extern void ExplainOneUtility(Node *utilityStmt, ExplainState *es,
const char *queryString, ParamListInfo params);
extern void ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
const char *queryString,
ParamListInfo params,
TupOutputState *tstate);
extern void ExplainOnePlan(PlannedStmt *plannedstmt, ExplainState *es,
const char *queryString, ParamListInfo params);
extern void ExplainPrintPlan(StringInfo str, QueryDesc *queryDesc,
bool analyze, bool verbose);
extern void ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc);
#endif /* EXPLAIN_H */
......@@ -6,14 +6,14 @@
*
* 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
#define PREPARE_H
#include "executor/executor.h"
#include "commands/explain.h"
#include "utils/plancache.h"
#include "utils/timestamp.h"
......@@ -40,9 +40,8 @@ extern void ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
ParamListInfo params,
DestReceiver *dest, char *completionTag);
extern void DeallocateQuery(DeallocateStmt *stmt);
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt,
const char *queryString,
ParamListInfo params, TupOutputState *tstate);
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainState *es,
const char *queryString, ParamListInfo params);
/* Low-level access to stored prepared statements */
extern void StorePreparedStatement(const char *stmt_name,
......
......@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* 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;
Node *query; /* the query (as a raw parse tree) */
bool verbose; /* print plan info */
bool analyze; /* get statistics by executing plan */
List *options; /* list of DefElem nodes */
} ExplainStmt;
/* ----------------------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment