]> granicus.if.org Git - postgresql/commitdiff
Implement EXPLAIN EXECUTE. By Neil Conway, with some kibitzing from
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 2 Feb 2003 23:46:38 +0000 (23:46 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 2 Feb 2003 23:46:38 +0000 (23:46 +0000)
Tom Lane.

doc/src/sgml/ref/execute.sgml
doc/src/sgml/ref/explain.sgml
doc/src/sgml/ref/prepare.sgml
doc/src/sgml/release.sgml
src/backend/commands/explain.c
src/backend/commands/prepare.c
src/backend/parser/gram.y
src/include/commands/explain.h
src/include/commands/prepare.h

index 2302ef99a328a0617486aa5e022e34cebf492e05..d62209797b3cd10570327f4e55ea8b6eb448fb57 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/execute.sgml,v 1.2 2003/01/19 00:13:29 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/execute.sgml,v 1.3 2003/02/02 23:46:37 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -99,9 +99,9 @@ PostgreSQL documentation
 
   <para>
    Like <command>SELECT INTO</command>, <command>EXECUTE</command> can
-   be used to store the results of executing the query in a table by
-   specifying an INTO clause. For more information on this behabior,
-   consult the reference for <xref linkend="sql-selectinto">.
+   store the results of executing the query into a newly-created
+   table, by specifying an INTO clause. For more information on this behavior,
+   see <xref linkend="sql-selectinto" endterm="sql-selectinto-title">.
   </para>
 
   <para>
index 7e7f43f50ea380592c928c5669fe7ab0dbe3ec29..14639cc268ce0e75c345f24aad7c63b21dca42d7 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/explain.sgml,v 1.22 2003/01/19 00:13:29 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/explain.sgml,v 1.23 2003/02/02 23:46:37 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -55,7 +55,8 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceabl
       <term><replaceable class="PARAMETER">query</replaceable></term>
       <listitem>
        <para>
-       Any <replaceable class="PARAMETER">query</replaceable>.
+       Any <command>SELECT</>, <command>INSERT</>, <command>UPDATE</>,
+       <command>DELETE</>, or <command>EXECUTE</> query.
        </para>
       </listitem>
      </varlistentry>
@@ -132,13 +133,13 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceabl
    <para>
     In order to allow the <productname>PostgreSQL</productname> query
     planner to make reasonably informed decisions when optimizing
-    queries, the <command>ANALYZE</command> statement should be used
+    queries, the <command>ANALYZE</command> statement should be run
     to record statistics about the distribution of data within the
-    table. If you have not done this (or the statistical distribution
+    table. If you have not done this (or if the statistical distribution
     of the data in the table has changed significantly since the last
-    time <command>ANALYZE</command> was run), the estimated costs and
-    the resulting query plan displayed by <command>EXPLAIN</command>
-    are unlikely to conform to the real properties of the query.
+    time <command>ANALYZE</command> was run), the estimated costs
+    are unlikely to conform to the real properties of the query,
+    and consequently an inferior query plan may be chosen.
    </para>
   </note>
 
@@ -147,7 +148,7 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceabl
    planned.  The total elapsed time expended within each plan node (in
    milliseconds) and total number of rows it actually returned are added to
    the display.  This is useful for seeing whether the planner's estimates
-   are close to the actual performance of the query.
+   are close to reality.
   </para>
 
   <caution>
@@ -157,8 +158,8 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceabl
     would return,
     other side-effects of the query will happen as usual.
     If you wish to use <command>EXPLAIN ANALYZE</command> on an INSERT,
-    UPDATE, or DELETE query without letting the query affect your data,
-    use this approach:
+    UPDATE, DELETE, or EXECUTE query without letting the query affect your
+    data, use this approach:
       <programlisting>
 BEGIN;
 EXPLAIN ANALYZE ...;
@@ -244,13 +245,35 @@ EXPLAIN SELECT sum(i) FROM foo WHERE i &lt; 10;
    </programlisting>
   </para>
 
+  <para>
+   Here is an example of using EXPLAIN EXECUTE to display the query
+   plan for a prepared query:
+
+   <programlisting>
+PREPARE query(int, int) AS SELECT sum(bar) FROM test
+    WHERE id &gt; $1 AND id &lt; $2
+    GROUP BY foo;
+
+EXPLAIN ANALYZE EXECUTE query(100, 200);
+    <computeroutput>
+                                                       QUERY PLAN                                                        
+-------------------------------------------------------------------------------------------------------------------------
+ HashAggregate  (cost=39.53..39.53 rows=1 width=8) (actual time=0.66..0.67 rows=7 loops=1)
+   -&gt;  Index Scan using test_pkey on test  (cost=0.00..32.97 rows=1311 width=8) (actual time=0.05..0.39 rows=99 loops=1)
+         Index Cond: ((id &gt; $1) AND (id &lt; $2))
+ Total runtime: 0.85 msec
+(4 rows)
+    </computeroutput>
+   </programlisting>
+  </para>
+
   <para>
    Note that the specific numbers shown, and even the selected query
    strategy, may vary between <productname>PostgreSQL</productname>
-   releases due to planner improvements. In addition, the algorithm
-   used by <command>ANALYZE</command> to generate statistics is not
-   completely deterministic; therefore, it is possible (although not
-   likely) for cost estimations to change between runs of
+   releases due to planner improvements. In addition, the
+   <command>ANALYZE</command> command uses random sampling to estimate
+   data statistics; therefore, it is possible
+   for cost estimates to change after a fresh run of
    <command>ANALYZE</command>, even if the actual distribution of data
    in the table has not changed.
   </para>
index d9fa86414bf1aabfd07a060db437a0e042b0570f..418bd83ace222495d9febd0f50ea2e240c5b69dc 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/prepare.sgml,v 1.1 2002/08/27 04:55:07 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/prepare.sgml,v 1.2 2003/02/02 23:46:37 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -156,7 +156,9 @@ PostgreSQL documentation
        constant values in a query to make guesses about the likely
        result of executing the query. Since this data is unavailable when
        planning prepared queries with parameters, the chosen plan may be
-       sub-optimal.
+       sub-optimal. To examine the query plan
+       <productname>PostgreSQL</productname> has chosen for a prepared
+       query, use <command>EXPLAIN EXECUTE</command>.
    </para>
 
    <para>
index fa1cac9f579da7407d88f8362822744ff268e179..354b70cc07359bb11bbb50ba299cd902faab216c 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.183 2003/02/02 19:48:20 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.184 2003/02/02 23:46:38 tgl Exp $
 -->
 
 <appendix id="release">
@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
 worries about funny characters.
 -->
 <literallayout><![CDATA[
+Can now do EXPLAIN ... EXECUTE to see plan used for a prepared query
 Explicit JOINs no longer constrain query plan, unless JOIN_COLLAPSE_LIMIT = 1
 Performance of "foo IN (SELECT ...)" queries has been considerably improved
 FETCH 0 now re-fetches cursor's current row, per SQL spec
index 3820dd8b4628d9a9bdf3decb2f6d72f3f7368d1c..6c8b02a156e4a15439f050eb629645cee9ede0a2 100644 (file)
@@ -1,20 +1,23 @@
-/*
+/*-------------------------------------------------------------------------
+ *
  * explain.c
- *       Explain the query execution plan
+ *       Explain query execution plans
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.99 2002/12/15 16:17:38 tgl Exp $
+ * IDENTIFICATION
+ *       $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.100 2003/02/02 23:46:38 tgl Exp $
  *
+ *-------------------------------------------------------------------------
  */
-
 #include "postgres.h"
 
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "catalog/pg_type.h"
 #include "commands/explain.h"
+#include "commands/prepare.h"
 #include "executor/executor.h"
 #include "executor/instrument.h"
 #include "lib/stringinfo.h"
@@ -81,8 +84,11 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest)
 
        if (query->commandType == CMD_UTILITY)
        {
-               /* rewriter will not cope with utility statements */
-               do_text_output_oneline(tstate, "Utility statements have no plan structure");
+               /* Rewriter will not cope with utility statements */
+               if (query->utilityStmt && IsA(query->utilityStmt, ExecuteStmt))
+                       ExplainExecuteQuery(stmt, tstate);
+               else
+                       do_text_output_oneline(tstate, "Utility statements have no plan structure");
        }
        else
        {
@@ -119,10 +125,6 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
 {
        Plan       *plan;
        QueryDesc  *queryDesc;
-       ExplainState *es;
-       StringInfo      str;
-       double          totaltime = 0;
-       struct timeval starttime;
 
        /* planner will not cope with utility statements */
        if (query->commandType == CMD_UTILITY)
@@ -134,6 +136,13 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
                return;
        }
 
+       /*
+        * We don't support DECLARE CURSOR in EXPLAIN, but parser will take it
+        * because it's an OptimizableStmt
+        */
+       if (query->isPortal)
+               elog(ERROR, "EXPLAIN / DECLARE CURSOR is not supported");
+
        /* plan the query */
        plan = planner(query);
 
@@ -141,15 +150,34 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
        if (plan == NULL)
                return;
 
-       /* We don't support DECLARE CURSOR here */
-       Assert(!query->isPortal);
-
-       gettimeofday(&starttime, NULL);
-
        /* Create a QueryDesc requesting no output */
        queryDesc = CreateQueryDesc(query, plan, None, NULL, NULL,
                                                                stmt->analyze);
 
+       ExplainOnePlan(queryDesc, stmt, tstate);
+}
+
+/*
+ * ExplainOnePlan -
+ *             given a planned query, execute it if needed, and then print
+ *             EXPLAIN output
+ *
+ * This is exported because it's called back from prepare.c in the
+ * EXPLAIN EXECUTE case
+ *
+ * Note: the passed-in QueryDesc is freed when we're done with it
+ */
+void
+ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
+                          TupOutputState *tstate)
+{
+       struct timeval starttime;
+       double          totaltime = 0;
+       ExplainState *es;
+       StringInfo      str;
+
+       gettimeofday(&starttime, NULL);
+
        /* call ExecutorStart to prepare the plan for execution */
        ExecutorStart(queryDesc);
 
@@ -160,7 +188,6 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
                ExecutorRun(queryDesc, ForwardScanDirection, 0L);
 
                /* We can't clean up 'till we're done printing the stats... */
-
                totaltime += elapsed_time(&starttime);
        }
 
@@ -169,14 +196,14 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
        es->printCost = true;           /* default */
        es->printNodes = stmt->verbose;
        es->printAnalyze = stmt->analyze;
-       es->rtable = query->rtable;
+       es->rtable = queryDesc->parsetree->rtable;
 
        if (es->printNodes)
        {
                char       *s;
                char       *f;
 
-               s = nodeToString(plan);
+               s = nodeToString(queryDesc->plantree);
                if (s)
                {
                        if (Explain_pretty_print)
@@ -195,7 +222,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
 
        if (es->printCost)
        {
-               explain_outNode(str, plan, queryDesc->planstate,
+               explain_outNode(str, queryDesc->plantree, queryDesc->planstate,
                                                NULL, 0, es);
        }
 
index 19d64c3e1c74ec8a6491f2ba780bd076860bcf6f..f6cd5d0a802f0fef4e5b48e5b2594630a6cc9506 100644 (file)
@@ -6,12 +6,13 @@
  * Copyright (c) 2002, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.12 2002/12/15 21:01:34 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.13 2003/02/02 23:46:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "commands/explain.h"
 #include "commands/prepare.h"
 #include "executor/executor.h"
 #include "utils/guc.h"
@@ -47,9 +48,10 @@ static HTAB *prepared_queries = NULL;
 
 static void InitQueryHashTable(void);
 static void StoreQuery(const char *stmt_name, List *query_list,
-                  List *plan_list, List *argtype_list);
+                                          List *plan_list, List *argtype_list);
 static QueryHashEntry *FetchQuery(const char *plan_name);
-
+static ParamListInfo EvaluateParams(EState *estate,
+                                                                       List *params, List *argtypes);
 
 /*
  * Implements the 'PREPARE' utility statement.
@@ -94,7 +96,7 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
                           *query_list,
                           *plan_list;
        ParamListInfo paramLI = NULL;
-       EState     *estate;
+       EState     *estate = NULL;
 
        /* Look it up in the hash table */
        entry = FetchQuery(stmt->name);
@@ -104,51 +106,22 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
 
        Assert(length(query_list) == length(plan_list));
 
-       /*
-        * Need an EState to evaluate parameters; must not delete it till end
-        * of query, in case parameters are pass-by-reference.
-        */
-       estate = CreateExecutorState();
-
        /* Evaluate parameters, if any */
        if (entry->argtype_list != NIL)
        {
-               int                     nargs = length(entry->argtype_list);
-               int                     i = 0;
-               List       *exprstates;
-
-               /* Parser should have caught this error, but check */
-               if (nargs != length(stmt->params))
-                       elog(ERROR, "ExecuteQuery: wrong number of arguments");
-
-               exprstates = (List *) ExecPrepareExpr((Expr *) stmt->params, estate);
-
-               paramLI = (ParamListInfo)
-                       palloc0((nargs + 1) * sizeof(ParamListInfoData));
-
-               foreach(l, exprstates)
-               {
-                       ExprState  *n = lfirst(l);
-                       bool            isNull;
-
-                       paramLI[i].value = ExecEvalExprSwitchContext(n,
-                                                                                        GetPerTupleExprContext(estate),
-                                                                                                                &isNull,
-                                                                                                                NULL);
-                       paramLI[i].kind = PARAM_NUM;
-                       paramLI[i].id = i + 1;
-                       paramLI[i].isnull = isNull;
-
-                       i++;
-               }
-               paramLI[i].kind = PARAM_INVALID;
+               /*
+                * Need an EState to evaluate parameters; must not delete it
+                * till end of query, in case parameters are pass-by-reference.
+                */
+               estate = CreateExecutorState();
+               paramLI = EvaluateParams(estate, stmt->params, entry->argtype_list);
        }
 
        /* Execute each query */
        foreach(l, query_list)
        {
-               Query      *query = lfirst(l);
-               Plan       *plan = lfirst(plan_list);
+               Query     *query = (Query *) lfirst(l);
+               Plan      *plan = (Plan *) lfirst(plan_list);
                bool            is_last_query;
 
                plan_list = lnext(plan_list);
@@ -196,11 +169,59 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
                        CommandCounterIncrement();
        }
 
-       FreeExecutorState(estate);
+       if (estate)
+               FreeExecutorState(estate);
 
        /* No need to pfree other memory, MemoryContext will be reset */
 }
 
+/*
+ * Evaluates a list of parameters, using the given executor state. It
+ * requires a list of the parameter values themselves, and a list of
+ * their types. It returns a filled-in ParamListInfo -- this can later
+ * be passed to CreateQueryDesc(), which allows the executor to make use
+ * of the parameters during query execution.
+ */
+static ParamListInfo
+EvaluateParams(EState *estate, List *params, List *argtypes)
+{
+       int                             nargs = length(argtypes);
+       ParamListInfo   paramLI;
+       List               *exprstates;
+       List               *l;
+       int                             i = 0;
+
+       /* Parser should have caught this error, but check anyway */
+       if (length(params) != nargs)
+               elog(ERROR, "EvaluateParams: wrong number of arguments");
+
+       exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
+
+       paramLI = (ParamListInfo)
+               palloc0((nargs + 1) * sizeof(ParamListInfoData));
+
+       foreach(l, exprstates)
+       {
+               ExprState  *n = lfirst(l);
+               bool            isNull;
+
+               paramLI[i].value = ExecEvalExprSwitchContext(n,
+                                                                                                        GetPerTupleExprContext(estate),
+                                                                                                        &isNull,
+                                                                                                        NULL);
+               paramLI[i].kind = PARAM_NUM;
+               paramLI[i].id = i + 1;
+               paramLI[i].isnull = isNull;
+
+               i++;
+       }
+
+       paramLI[i].kind = PARAM_INVALID;
+
+       return paramLI;
+}
+
+
 /*
  * Initialize query hash table upon first use.
  */
@@ -229,8 +250,8 @@ InitQueryHashTable(void)
  * to the hash entry, so the caller can dispose of their copy.
  */
 static void
-StoreQuery(const char *stmt_name, List *query_list, List *plan_list,
-                  List *argtype_list)
+StoreQuery(const char *stmt_name, List *query_list,
+                  List *plan_list, List *argtype_list)
 {
        QueryHashEntry *entry;
        MemoryContext oldcxt,
@@ -278,7 +299,7 @@ StoreQuery(const char *stmt_name, List *query_list, List *plan_list,
                                                                                   HASH_ENTER,
                                                                                   &found);
 
-       /* Shouldn't get a failure, nor duplicate entry */
+       /* Shouldn't get a failure, nor duplicate entry */
        if (!entry || found)
                elog(ERROR, "Unable to store prepared statement \"%s\"!",
                         stmt_name);
@@ -293,7 +314,8 @@ StoreQuery(const char *stmt_name, List *query_list, List *plan_list,
 }
 
 /*
- * Lookup an existing query in the hash table.
+ * Lookup an existing query in the hash table. If the query does not
+ * actually exist, an elog(ERROR) is thrown.
  */
 static QueryHashEntry *
 FetchQuery(const char *plan_name)
@@ -346,52 +368,104 @@ FetchQueryParams(const char *plan_name)
 /*
  * Implements the 'DEALLOCATE' utility statement: deletes the
  * specified plan from storage.
- *
- * The initial part of this routine is identical to FetchQuery(),
- * but we repeat the coding because we need to use the key twice.
  */
 void
 DeallocateQuery(DeallocateStmt *stmt)
 {
-       char            key[HASH_KEY_LEN];
        QueryHashEntry *entry;
 
-       /*
-        * If the hash table hasn't been initialized, it can't be storing
-        * anything, therefore it couldn't possibly store our plan.
-        */
-       if (!prepared_queries)
-               elog(ERROR, "Prepared statement with name \"%s\" does not exist",
-                        stmt->name);
-
-       /*
-        * We can't just use the statement name as supplied by the user: the
-        * hash package is picky enough that it needs to be NULL-padded out to
-        * the appropriate length to work correctly.
-        */
-       MemSet(key, 0, sizeof(key));
-       strncpy(key, stmt->name, sizeof(key));
-
-       /*
-        * First lookup the entry, so we can release all the subsidiary memory
-        * it has allocated (when it's removed, hash_search() will return a
-        * dangling pointer, so it needs to be done prior to HASH_REMOVE).
-        * This requires an extra hash-table lookup, but DEALLOCATE isn't
-        * exactly a performance bottleneck.
-        */
-       entry = (QueryHashEntry *) hash_search(prepared_queries,
-                                                                                  key,
-                                                                                  HASH_FIND,
-                                                                                  NULL);
-
-       if (!entry)
-               elog(ERROR, "Prepared statement with name \"%s\" does not exist",
-                        stmt->name);
+       /* Find the query's hash table entry */
+       entry = FetchQuery(stmt->name);
 
        /* Flush the context holding the subsidiary data */
        Assert(MemoryContextIsValid(entry->context));
        MemoryContextDelete(entry->context);
 
        /* Now we can remove the hash table entry */
-       hash_search(prepared_queries, key, HASH_REMOVE, NULL);
+       hash_search(prepared_queries, entry->key, HASH_REMOVE, NULL);
+}
+
+/*
+ * Implements the 'EXPLAIN EXECUTE' utility statement.
+ */
+void
+ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
+{
+       ExecuteStmt        *execstmt = (ExecuteStmt *) stmt->query->utilityStmt;
+       QueryHashEntry *entry;
+       List       *l,
+                          *query_list,
+                          *plan_list;
+       ParamListInfo paramLI = NULL;
+       EState     *estate = NULL;
+
+       /* explain.c should only call me for EXECUTE stmt */
+       Assert(execstmt && IsA(execstmt, ExecuteStmt));
+
+       /* Look it up in the hash table */
+       entry = FetchQuery(execstmt->name);
+
+       query_list = entry->query_list;
+       plan_list = entry->plan_list;
+
+       Assert(length(query_list) == length(plan_list));
+
+       /* Evaluate parameters, if any */
+       if (entry->argtype_list != NIL)
+       {
+               /*
+                * Need an EState to evaluate parameters; must not delete it
+                * till end of query, in case parameters are pass-by-reference.
+                */
+               estate = CreateExecutorState();
+               paramLI = EvaluateParams(estate, execstmt->params,
+                                                                entry->argtype_list);
+       }
+
+       /* Explain each query */
+       foreach(l, query_list)
+       {
+               Query     *query = (Query *) lfirst(l);
+               Plan      *plan = (Plan *) lfirst(plan_list);
+               bool            is_last_query;
+
+               plan_list = lnext(plan_list);
+               is_last_query = (plan_list == NIL);
+
+               if (query->commandType == CMD_UTILITY)
+               {
+                       if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
+                               do_text_output_oneline(tstate, "NOTIFY");
+                       else
+                               do_text_output_oneline(tstate, "UTILITY");
+               }
+               else
+               {
+                       QueryDesc  *qdesc;
+
+                       /* Create a QueryDesc requesting no output */
+                       qdesc = CreateQueryDesc(query, plan, None, NULL,
+                                                                       paramLI, stmt->analyze);
+
+                       if (execstmt->into)
+                       {
+                               if (qdesc->operation != CMD_SELECT)
+                                       elog(ERROR, "INTO clause specified for non-SELECT query");
+
+                               query->into = execstmt->into;
+                               qdesc->dest = None;
+                       }
+
+                       ExplainOnePlan(qdesc, stmt, tstate);
+               }
+
+               /* No need for CommandCounterIncrement, as ExplainOnePlan did it */
+
+               /* put a blank line between plans */
+               if (!is_last_query)
+                       do_text_output_oneline(tstate, "");
+       }
+
+       if (estate)
+               FreeExecutorState(estate);
 }
index ff79e7a39f841908394a974c905d1aea48fadced..2d3c4445206a0c4058f7f342b8f7caab4902987c 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.396 2003/01/23 23:38:56 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.397 2003/02/02 23:46:38 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -241,7 +241,7 @@ static void doNegateFloat(Value *v);
 %type <ival>   opt_interval
 %type <node>   overlay_placing substr_from substr_for
 
-%type <boolean> opt_instead opt_cursor
+%type <boolean> opt_instead opt_cursor opt_analyze
 %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
@@ -3953,30 +3953,34 @@ opt_name_list:
 /*****************************************************************************
  *
  *             QUERY:
- *                             EXPLAIN query
- *                             EXPLAIN ANALYZE query
+ *                             EXPLAIN [ANALYZE] [VERBOSE] query
  *
  *****************************************************************************/
 
 ExplainStmt:
-                       EXPLAIN opt_verbose OptimizableStmt
+                       EXPLAIN opt_analyze opt_verbose OptimizableStmt
                                {
                                        ExplainStmt *n = makeNode(ExplainStmt);
-                                       n->verbose = $2;
-                                       n->analyze = FALSE;
-                                       n->query = (Query*)$3;
+                                       n->analyze = $2;
+                                       n->verbose = $3;
+                                       n->query = (Query*)$4;
                                        $$ = (Node *)n;
                                }
-                       | EXPLAIN analyze_keyword opt_verbose OptimizableStmt
+                       | EXPLAIN opt_analyze opt_verbose ExecuteStmt
                                {
                                        ExplainStmt *n = makeNode(ExplainStmt);
+                                       n->analyze = $2;
                                        n->verbose = $3;
-                                       n->analyze = TRUE;
                                        n->query = (Query*)$4;
                                        $$ = (Node *)n;
                                }
                ;
 
+opt_analyze:
+                       analyze_keyword                 { $$ = TRUE; }
+                       | /* EMPTY */                   { $$ = FALSE; }
+               ;
+
 /*****************************************************************************
  *
  *             QUERY:
index 7889ccccfb8486544b364852a81f52426b753880..040740490bd4389abd9c7a6eacfb29b7444c5374 100644 (file)
@@ -6,16 +6,20 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
- * $Id: explain.h,v 1.17 2002/06/20 20:29:49 momjian Exp $
+ * $Id: explain.h,v 1.18 2003/02/02 23:46:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef EXPLAIN_H
 #define EXPLAIN_H
 
+#include "executor/executor.h"
 #include "nodes/parsenodes.h"
 #include "tcop/dest.h"
 
+
 extern void ExplainQuery(ExplainStmt *stmt, CommandDest dest);
+extern void ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
+                                                  TupOutputState *tstate);
 
 #endif   /* EXPLAIN_H */
index 884236ff99eada83f020c2c283e6d966b8bd8b3e..aad641667574af69ca62595c74c4ee3cc0013ff8 100644 (file)
@@ -6,24 +6,22 @@
  *
  * Copyright (c) 2002, PostgreSQL Global Development Group
  *
- * $Id: prepare.h,v 1.2 2002/09/04 20:31:42 momjian Exp $
+ * $Id: prepare.h,v 1.3 2003/02/02 23:46:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-
 #ifndef PREPARE_H
 #define PREPARE_H
 
+#include "executor/executor.h"
 #include "nodes/parsenodes.h"
 #include "tcop/dest.h"
 
 
 extern void PrepareQuery(PrepareStmt *stmt);
-
 extern void ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest);
-
 extern void DeallocateQuery(DeallocateStmt *stmt);
-
 extern List *FetchQueryParams(const char *plan_name);
+extern void ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate);
 
 #endif   /* PREPARE_H */