Extend EXPLAIN to allow generic options to be specified.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 26 Jul 2009 23:34:18 +0000 (23:34 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 26 Jul 2009 23:34:18 +0000 (23:34 +0000)
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

12 files changed:
contrib/auto_explain/auto_explain.c
doc/src/sgml/ref/explain.sgml
src/backend/commands/define.c
src/backend/commands/explain.c
src/backend/commands/prepare.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/tcop/utility.c
src/include/commands/explain.h
src/include/commands/prepare.h
src/include/nodes/parsenodes.h

index b7c0ef929cca5e8b113501161dfb522eccda87d2..a711b7b118cde6d6090415778d1a8b0408d01360 100644 (file)
@@ -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);
                }
        }
 
index ea9b3a6053ce173f1cab9dff399c5b45e7b1e911..0183816fd3a097d70b59ceff2dfea39b028313a1 100644 (file)
@@ -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:
index 2d6343aa1960a3c0fe1f22edce89682cdd122287..4965b3881d7e7ad4cd3fc17c7f0946891a1e83f4 100644 (file)
@@ -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;
        }
index fab7ef95004020d9250c6d95ac9a9deda159bce0..fbc1334eb5ef62cf577e8d7b62909785b52f0fcc 100644 (file)
@@ -16,6 +16,7 @@
 #include "access/xact.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_type.h"
+#include "commands/defrem.h"
 #include "commands/explain.h"
 #include "commands/prepare.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;
 
 
-typedef struct ExplainState
-{
-       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 ExplainOneQuery(Query *query, ExplainState *es,
+                               const char *queryString, ParamListInfo params);
 static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
                                StringInfo buf);
 static double elapsed_time(instr_time *starttime);
@@ -84,11 +73,33 @@ void
 ExplainQuery(ExplainStmt *stmt, const char *queryString,
                         ParamListInfo params, DestReceiver *dest)
 {
+       ExplainState es;
        Oid                *param_types;
        int                     num_params;
        TupOutputState *tstate;
        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 */
        getParamListTypes(params, &param_types, &num_params);
@@ -106,28 +117,44 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
        rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query),
                                                                           queryString, param_types, num_params);
 
-       /* prepare for projection of tuples */
-       tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt));
-
        if (rewritten == NIL)
        {
                /* 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
        {
+               ListCell   *l;
+
                /* Explain every plan */
                foreach(l, rewritten)
                {
-                       ExplainOneQuery((Query *) lfirst(l), stmt,
-                                                       queryString, params, tstate);
+                       ExplainOneQuery((Query *) lfirst(l), &es, queryString, params);
                        /* put a blank line between plans */
                        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);
+
+       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
  */
 static void
-ExplainOneQuery(Query *query, ExplainStmt *stmt, const char *queryString,
-                               ParamListInfo params, TupOutputState *tstate)
+ExplainOneQuery(Query *query, ExplainState *es,
+                               const char *queryString, ParamListInfo params)
 {
        /* planner will not cope with utility statements */
        if (query->commandType == CMD_UTILITY)
        {
-               ExplainOneUtility(query->utilityStmt, stmt,
-                                                 queryString, params, tstate);
+               ExplainOneUtility(query->utilityStmt, es, queryString, params);
                return;
        }
 
        /* if an advisor plugin is present, let it manage things */
        if (ExplainOneQuery_hook)
-               (*ExplainOneQuery_hook) (query, stmt, queryString, params, tstate);
+               (*ExplainOneQuery_hook) (query, es, queryString, params);
        else
        {
                PlannedStmt *plan;
@@ -173,7 +199,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, const char *queryString,
                plan = pg_plan_query(query, 0, params);
 
                /* 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
  */
 void
-ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt,
-                                 const char *queryString, ParamListInfo params,
-                                 TupOutputState *tstate)
+ExplainOneUtility(Node *utilityStmt, ExplainState *es,
+                                 const char *queryString, ParamListInfo params)
 {
        if (utilityStmt == NULL)
                return;
 
        if (IsA(utilityStmt, ExecuteStmt))
-               ExplainExecuteQuery((ExecuteStmt *) utilityStmt, stmt,
-                                                       queryString, params, tstate);
+               ExplainExecuteQuery((ExecuteStmt *) utilityStmt, es,
+                                                       queryString, params);
        else if (IsA(utilityStmt, NotifyStmt))
-               do_text_output_oneline(tstate, "NOTIFY");
+               appendStringInfoString(es->str, "NOTIFY\n");
        else
-               do_text_output_oneline(tstate,
-                                                          "Utility statements have no plan structure");
+               appendStringInfoString(es->str,
+                                                          "Utility statements have no plan structure\n");
 }
 
 /*
@@ -219,14 +244,12 @@ ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt,
  * to call it.
  */
 void
-ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
-                          const char *queryString, ParamListInfo params,
-                          TupOutputState *tstate)
+ExplainOnePlan(PlannedStmt *plannedstmt, ExplainState *es,
+                          const char *queryString, ParamListInfo params)
 {
        QueryDesc  *queryDesc;
        instr_time      starttime;
        double          totaltime = 0;
-       StringInfoData buf;
        int                     eflags;
 
        /*
@@ -238,17 +261,16 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
        /* Create a QueryDesc requesting no output */
        queryDesc = CreateQueryDesc(plannedstmt, queryString,
                                                                GetActiveSnapshot(), InvalidSnapshot,
-                                                               None_Receiver, params,
-                                                               stmt->analyze);
+                                                               None_Receiver, params, es->analyze);
 
        INSTR_TIME_SET_CURRENT(starttime);
 
        /* If analyzing, we need to cope with queued triggers */
-       if (stmt->analyze)
+       if (es->analyze)
                AfterTriggerBeginQuery();
 
        /* Select execution options */
-       if (stmt->analyze)
+       if (es->analyze)
                eflags = 0;                             /* default run-to-completion flags */
        else
                eflags = EXEC_FLAG_EXPLAIN_ONLY;
@@ -257,7 +279,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
        ExecutorStart(queryDesc, eflags);
 
        /* Execute the plan for statistics if asked for */
-       if (stmt->analyze)
+       if (es->analyze)
        {
                /* run the plan */
                ExecutorRun(queryDesc, ForwardScanDirection, 0L);
@@ -267,15 +289,14 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
        }
 
        /* Create textual dump of plan tree */
-       initStringInfo(&buf);
-       ExplainPrintPlan(&buf, queryDesc, stmt->analyze, stmt->verbose);
+       ExplainPrintPlan(es, queryDesc);
 
        /*
         * 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
         * transaction, we can't measure them.)  Include into total runtime.
         */
-       if (stmt->analyze)
+       if (es->analyze)
        {
                INSTR_TIME_SET_CURRENT(starttime);
                AfterTriggerEndQuery(queryDesc->estate);
@@ -283,7 +304,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
        }
 
        /* Print info about runtime of triggers */
-       if (stmt->analyze)
+       if (es->analyze)
        {
                ResultRelInfo *rInfo;
                bool            show_relname;
@@ -295,12 +316,12 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
                show_relname = (numrels > 1 || targrels != NIL);
                rInfo = queryDesc->estate->es_result_relations;
                for (nr = 0; nr < numrels; rInfo++, nr++)
-                       report_triggers(rInfo, show_relname, &buf);
+                       report_triggers(rInfo, show_relname, es->str);
 
                foreach(l, targrels)
                {
                        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();
 
        /* We need a CCI just in case query expanded to multiple plans */
-       if (stmt->analyze)
+       if (es->analyze)
                CommandCounterIncrement();
 
        totaltime += elapsed_time(&starttime);
 
-       if (stmt->analyze)
-               appendStringInfo(&buf, "Total runtime: %.3f ms\n",
+       if (es->analyze)
+               appendStringInfo(es->str, "Total runtime: %.3f ms\n",
                                                 1000.0 * totaltime);
-       do_text_output_multiline(tstate, buf.data);
-
-       pfree(buf.data);
 }
 
 /*
  * 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
- * 'verbose' means a verbose printout (currently, it shows targetlists)
+ * The caller should have set up the options fields of *es, as well as
+ * initializing the output buffer es->str.  Other fields in *es are
+ * initialized here.
  *
  * NB: will not work on utility statements
  */
 void
-ExplainPrintPlan(StringInfo str, QueryDesc *queryDesc,
-                                bool analyze, bool verbose)
+ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
 {
-       ExplainState es;
-
        Assert(queryDesc->plannedstmt != NULL);
-
-       memset(&es, 0, sizeof(es));
-       es.str = str;
-       es.printTList = verbose;
-       es.printAnalyze = analyze;
-       es.pstmt = queryDesc->plannedstmt;
-       es.rtable = queryDesc->plannedstmt->rtable;
-
+       es->pstmt = queryDesc->plannedstmt;
+       es->rtable = queryDesc->plannedstmt->rtable;
        ExplainNode(queryDesc->plannedstmt->planTree, queryDesc->planstate,
-                               NULL, 0, &es);
+                               NULL, 0, es);
 }
 
 /*
@@ -692,9 +702,10 @@ ExplainNode(Plan *plan, PlanState *planstate,
                        break;
        }
 
-       appendStringInfo(es->str, "  (cost=%.2f..%.2f rows=%.0f width=%d)",
-                                        plan->startup_cost, plan->total_cost,
-                                        plan->plan_rows, plan->plan_width);
+       if (es->costs)
+               appendStringInfo(es->str, "  (cost=%.2f..%.2f rows=%.0f width=%d)",
+                                                plan->startup_cost, plan->total_cost,
+                                                plan->plan_rows, plan->plan_width);
 
        /*
         * 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->nloops);
        }
-       else if (es->printAnalyze)
+       else if (es->analyze)
                appendStringInfoString(es->str, " (never executed)");
        appendStringInfoChar(es->str, '\n');
 
        /* target list */
-       if (es->printTList)
+       if (es->verbose)
                show_plan_tlist(plan, indent, es);
 
        /* quals, sort keys, etc */
@@ -1025,7 +1036,7 @@ static void
 show_sort_info(SortState *sortstate, int indent, ExplainState *es)
 {
        Assert(IsA(sortstate, SortState));
-       if (es->printAnalyze && sortstate->sort_Done &&
+       if (es->analyze && sortstate->sort_Done &&
                sortstate->tuplesortstate != NULL)
        {
                char       *sortinfo;
index 1299e1da18781ff131a0107a296d839edcce6ede..cfe97f56de62bde6f07ae4ee3c03874da95e40bf 100644 (file)
@@ -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)
index e1cd3fea401ac1463b296acf2330e5c6fe45ccec..7b7383b248b0bf94fb843540c4c3eb53f206ebce 100644 (file)
@@ -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;
 }
index 7c541bc0356de313b2737590314b8c82863d302c..33d5db0086f84937e4b572f8b5d7aa30ad690603 100644 (file)
@@ -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;
 }
index 38ee5585c0d01e08765d3dde202e1fcadbca09c7..58bb8d1369365f83d2890921c9e71173a8ebda71 100644 (file)
@@ -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; }
                ;
 
 /*****************************************************************************
index c9f2b87c14a88511085d649c7eccc5ddb745326c..04d6c4c8d72cccfc55447ccf8183284c5150461d 100644 (file)
@@ -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;
                        }
index aa3b643b4ee1e9ecb442daef6a4338f2bc77efcc..fdea30ae65bcb2b36355ad38938c30d6d5b6c23f 100644 (file)
 
 #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 */
index bf16bd347f28ac09b738f35eec9e7129795248a5..e6f770954fb9d8ceafc75a3c5f6b4bed7b50fd04 100644 (file)
@@ -13,7 +13,7 @@
 #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,
index 183a6c73a1ad822a58d7503a8ded1cf39e3afda2..f7e6939d6275dd06689467c75677bd5df8e04d1b 100644 (file)
@@ -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;
 
 /* ----------------------