Commit c7bceca1 authored by Tom Lane's avatar Tom Lane

Implement EXPLAIN EXECUTE. By Neil Conway, with some kibitzing from

Tom Lane.
parent 6adb475f
<!-- <!--
$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 PostgreSQL documentation
--> -->
...@@ -99,9 +99,9 @@ PostgreSQL documentation ...@@ -99,9 +99,9 @@ PostgreSQL documentation
<para> <para>
Like <command>SELECT INTO</command>, <command>EXECUTE</command> can Like <command>SELECT INTO</command>, <command>EXECUTE</command> can
be used to store the results of executing the query in a table by store the results of executing the query into a newly-created
specifying an INTO clause. For more information on this behabior, table, by specifying an INTO clause. For more information on this behavior,
consult the reference for <xref linkend="sql-selectinto">. see <xref linkend="sql-selectinto" endterm="sql-selectinto-title">.
</para> </para>
<para> <para>
......
<!-- <!--
$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 PostgreSQL documentation
--> -->
...@@ -55,7 +55,8 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceabl ...@@ -55,7 +55,8 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceabl
<term><replaceable class="PARAMETER">query</replaceable></term> <term><replaceable class="PARAMETER">query</replaceable></term>
<listitem> <listitem>
<para> <para>
Any <replaceable class="PARAMETER">query</replaceable>. Any <command>SELECT</>, <command>INSERT</>, <command>UPDATE</>,
<command>DELETE</>, or <command>EXECUTE</> query.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -132,13 +133,13 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceabl ...@@ -132,13 +133,13 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceabl
<para> <para>
In order to allow the <productname>PostgreSQL</productname> query In order to allow the <productname>PostgreSQL</productname> query
planner to make reasonably informed decisions when optimizing 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 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 of the data in the table has changed significantly since the last
time <command>ANALYZE</command> was run), the estimated costs and time <command>ANALYZE</command> was run), the estimated costs
the resulting query plan displayed by <command>EXPLAIN</command> are unlikely to conform to the real properties of the query,
are unlikely to conform to the real properties of the query. and consequently an inferior query plan may be chosen.
</para> </para>
</note> </note>
...@@ -147,7 +148,7 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceabl ...@@ -147,7 +148,7 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceabl
planned. The total elapsed time expended within each plan node (in planned. The total elapsed time expended within each plan node (in
milliseconds) and total number of rows it actually returned are added to milliseconds) and total number of rows it actually returned are added to
the display. This is useful for seeing whether the planner's estimates 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> </para>
<caution> <caution>
...@@ -157,8 +158,8 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceabl ...@@ -157,8 +158,8 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceabl
would return, would return,
other side-effects of the query will happen as usual. other side-effects of the query will happen as usual.
If you wish to use <command>EXPLAIN ANALYZE</command> on an INSERT, If you wish to use <command>EXPLAIN ANALYZE</command> on an INSERT,
UPDATE, or DELETE query without letting the query affect your data, UPDATE, DELETE, or EXECUTE query without letting the query affect your
use this approach: data, use this approach:
<programlisting> <programlisting>
BEGIN; BEGIN;
EXPLAIN ANALYZE ...; EXPLAIN ANALYZE ...;
...@@ -244,13 +245,35 @@ EXPLAIN SELECT sum(i) FROM foo WHERE i &lt; 10; ...@@ -244,13 +245,35 @@ EXPLAIN SELECT sum(i) FROM foo WHERE i &lt; 10;
</programlisting> </programlisting>
</para> </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> <para>
Note that the specific numbers shown, and even the selected query Note that the specific numbers shown, and even the selected query
strategy, may vary between <productname>PostgreSQL</productname> strategy, may vary between <productname>PostgreSQL</productname>
releases due to planner improvements. In addition, the algorithm releases due to planner improvements. In addition, the
used by <command>ANALYZE</command> to generate statistics is not <command>ANALYZE</command> command uses random sampling to estimate
completely deterministic; therefore, it is possible (although not data statistics; therefore, it is possible
likely) for cost estimations to change between runs of for cost estimates to change after a fresh run of
<command>ANALYZE</command>, even if the actual distribution of data <command>ANALYZE</command>, even if the actual distribution of data
in the table has not changed. in the table has not changed.
</para> </para>
......
<!-- <!--
$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 PostgreSQL documentation
--> -->
...@@ -156,7 +156,9 @@ PostgreSQL documentation ...@@ -156,7 +156,9 @@ PostgreSQL documentation
constant values in a query to make guesses about the likely constant values in a query to make guesses about the likely
result of executing the query. Since this data is unavailable when result of executing the query. Since this data is unavailable when
planning prepared queries with parameters, the chosen plan may be 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>
<para> <para>
......
<!-- <!--
$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"> <appendix id="release">
...@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without ...@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
worries about funny characters. worries about funny characters.
--> -->
<literallayout><![CDATA[ <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 Explicit JOINs no longer constrain query plan, unless JOIN_COLLAPSE_LIMIT = 1
Performance of "foo IN (SELECT ...)" queries has been considerably improved Performance of "foo IN (SELECT ...)" queries has been considerably improved
FETCH 0 now re-fetches cursor's current row, per SQL spec FETCH 0 now re-fetches cursor's current row, per SQL spec
......
/* /*-------------------------------------------------------------------------
*
* explain.c * explain.c
* Explain the query execution plan * Explain query execution plans
* *
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, 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
* *
* $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 "postgres.h"
#include "access/genam.h" #include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/explain.h" #include "commands/explain.h"
#include "commands/prepare.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "executor/instrument.h" #include "executor/instrument.h"
#include "lib/stringinfo.h" #include "lib/stringinfo.h"
...@@ -81,8 +84,11 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest) ...@@ -81,8 +84,11 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest)
if (query->commandType == CMD_UTILITY) if (query->commandType == CMD_UTILITY)
{ {
/* rewriter will not cope with utility statements */ /* Rewriter will not cope with utility statements */
do_text_output_oneline(tstate, "Utility statements have no plan structure"); if (query->utilityStmt && IsA(query->utilityStmt, ExecuteStmt))
ExplainExecuteQuery(stmt, tstate);
else
do_text_output_oneline(tstate, "Utility statements have no plan structure");
} }
else else
{ {
...@@ -119,10 +125,6 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate) ...@@ -119,10 +125,6 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
{ {
Plan *plan; Plan *plan;
QueryDesc *queryDesc; QueryDesc *queryDesc;
ExplainState *es;
StringInfo str;
double totaltime = 0;
struct timeval starttime;
/* planner will not cope with utility statements */ /* planner will not cope with utility statements */
if (query->commandType == CMD_UTILITY) if (query->commandType == CMD_UTILITY)
...@@ -134,6 +136,13 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate) ...@@ -134,6 +136,13 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
return; 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 the query */
plan = planner(query); plan = planner(query);
...@@ -141,15 +150,34 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate) ...@@ -141,15 +150,34 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
if (plan == NULL) if (plan == NULL)
return; return;
/* We don't support DECLARE CURSOR here */
Assert(!query->isPortal);
gettimeofday(&starttime, NULL);
/* Create a QueryDesc requesting no output */ /* Create a QueryDesc requesting no output */
queryDesc = CreateQueryDesc(query, plan, None, NULL, NULL, queryDesc = CreateQueryDesc(query, plan, None, NULL, NULL,
stmt->analyze); 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 */ /* call ExecutorStart to prepare the plan for execution */
ExecutorStart(queryDesc); ExecutorStart(queryDesc);
...@@ -160,7 +188,6 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate) ...@@ -160,7 +188,6 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
ExecutorRun(queryDesc, ForwardScanDirection, 0L); ExecutorRun(queryDesc, ForwardScanDirection, 0L);
/* We can't clean up 'till we're done printing the stats... */ /* We can't clean up 'till we're done printing the stats... */
totaltime += elapsed_time(&starttime); totaltime += elapsed_time(&starttime);
} }
...@@ -169,14 +196,14 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate) ...@@ -169,14 +196,14 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
es->printCost = true; /* default */ es->printCost = true; /* default */
es->printNodes = stmt->verbose; es->printNodes = stmt->verbose;
es->printAnalyze = stmt->analyze; es->printAnalyze = stmt->analyze;
es->rtable = query->rtable; es->rtable = queryDesc->parsetree->rtable;
if (es->printNodes) if (es->printNodes)
{ {
char *s; char *s;
char *f; char *f;
s = nodeToString(plan); s = nodeToString(queryDesc->plantree);
if (s) if (s)
{ {
if (Explain_pretty_print) if (Explain_pretty_print)
...@@ -195,7 +222,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate) ...@@ -195,7 +222,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
if (es->printCost) if (es->printCost)
{ {
explain_outNode(str, plan, queryDesc->planstate, explain_outNode(str, queryDesc->plantree, queryDesc->planstate,
NULL, 0, es); NULL, 0, es);
} }
......
...@@ -6,12 +6,13 @@ ...@@ -6,12 +6,13 @@
* Copyright (c) 2002, PostgreSQL Global Development Group * Copyright (c) 2002, PostgreSQL Global Development Group
* *
* IDENTIFICATION * 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 "postgres.h"
#include "commands/explain.h"
#include "commands/prepare.h" #include "commands/prepare.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "utils/guc.h" #include "utils/guc.h"
...@@ -47,9 +48,10 @@ static HTAB *prepared_queries = NULL; ...@@ -47,9 +48,10 @@ static HTAB *prepared_queries = NULL;
static void InitQueryHashTable(void); static void InitQueryHashTable(void);
static void StoreQuery(const char *stmt_name, List *query_list, 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 QueryHashEntry *FetchQuery(const char *plan_name);
static ParamListInfo EvaluateParams(EState *estate,
List *params, List *argtypes);
/* /*
* Implements the 'PREPARE' utility statement. * Implements the 'PREPARE' utility statement.
...@@ -94,7 +96,7 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest) ...@@ -94,7 +96,7 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
*query_list, *query_list,
*plan_list; *plan_list;
ParamListInfo paramLI = NULL; ParamListInfo paramLI = NULL;
EState *estate; EState *estate = NULL;
/* Look it up in the hash table */ /* Look it up in the hash table */
entry = FetchQuery(stmt->name); entry = FetchQuery(stmt->name);
...@@ -104,51 +106,22 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest) ...@@ -104,51 +106,22 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
Assert(length(query_list) == length(plan_list)); 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 */ /* Evaluate parameters, if any */
if (entry->argtype_list != NIL) if (entry->argtype_list != NIL)
{ {
int nargs = length(entry->argtype_list); /*
int i = 0; * Need an EState to evaluate parameters; must not delete it
List *exprstates; * till end of query, in case parameters are pass-by-reference.
*/
/* Parser should have caught this error, but check */ estate = CreateExecutorState();
if (nargs != length(stmt->params)) paramLI = EvaluateParams(estate, stmt->params, entry->argtype_list);
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;
} }
/* Execute each query */ /* Execute each query */
foreach(l, query_list) foreach(l, query_list)
{ {
Query *query = lfirst(l); Query *query = (Query *) lfirst(l);
Plan *plan = lfirst(plan_list); Plan *plan = (Plan *) lfirst(plan_list);
bool is_last_query; bool is_last_query;
plan_list = lnext(plan_list); plan_list = lnext(plan_list);
...@@ -196,11 +169,59 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest) ...@@ -196,11 +169,59 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
CommandCounterIncrement(); CommandCounterIncrement();
} }
FreeExecutorState(estate); if (estate)
FreeExecutorState(estate);
/* No need to pfree other memory, MemoryContext will be reset */ /* 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. * Initialize query hash table upon first use.
*/ */
...@@ -229,8 +250,8 @@ InitQueryHashTable(void) ...@@ -229,8 +250,8 @@ InitQueryHashTable(void)
* to the hash entry, so the caller can dispose of their copy. * to the hash entry, so the caller can dispose of their copy.
*/ */
static void static void
StoreQuery(const char *stmt_name, List *query_list, List *plan_list, StoreQuery(const char *stmt_name, List *query_list,
List *argtype_list) List *plan_list, List *argtype_list)
{ {
QueryHashEntry *entry; QueryHashEntry *entry;
MemoryContext oldcxt, MemoryContext oldcxt,
...@@ -278,7 +299,7 @@ StoreQuery(const char *stmt_name, List *query_list, List *plan_list, ...@@ -278,7 +299,7 @@ StoreQuery(const char *stmt_name, List *query_list, List *plan_list,
HASH_ENTER, HASH_ENTER,
&found); &found);
/* Shouldn't get a failure, nor duplicate entry */ /* Shouldn't get a failure, nor a duplicate entry */
if (!entry || found) if (!entry || found)
elog(ERROR, "Unable to store prepared statement \"%s\"!", elog(ERROR, "Unable to store prepared statement \"%s\"!",
stmt_name); stmt_name);
...@@ -293,7 +314,8 @@ StoreQuery(const char *stmt_name, List *query_list, List *plan_list, ...@@ -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 * static QueryHashEntry *
FetchQuery(const char *plan_name) FetchQuery(const char *plan_name)
...@@ -346,52 +368,104 @@ FetchQueryParams(const char *plan_name) ...@@ -346,52 +368,104 @@ FetchQueryParams(const char *plan_name)
/* /*
* Implements the 'DEALLOCATE' utility statement: deletes the * Implements the 'DEALLOCATE' utility statement: deletes the
* specified plan from storage. * 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 void
DeallocateQuery(DeallocateStmt *stmt) DeallocateQuery(DeallocateStmt *stmt)
{ {
char key[HASH_KEY_LEN];
QueryHashEntry *entry; QueryHashEntry *entry;
/* /* Find the query's hash table entry */
* If the hash table hasn't been initialized, it can't be storing entry = FetchQuery(stmt->name);
* 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);
/* Flush the context holding the subsidiary data */ /* Flush the context holding the subsidiary data */
Assert(MemoryContextIsValid(entry->context)); Assert(MemoryContextIsValid(entry->context));
MemoryContextDelete(entry->context); MemoryContextDelete(entry->context);
/* Now we can remove the hash table entry */ /* 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);
} }
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -241,7 +241,7 @@ static void doNegateFloat(Value *v); ...@@ -241,7 +241,7 @@ static void doNegateFloat(Value *v);
%type <ival> opt_interval %type <ival> opt_interval
%type <node> overlay_placing substr_from substr_for %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> 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
...@@ -3953,30 +3953,34 @@ opt_name_list: ...@@ -3953,30 +3953,34 @@ opt_name_list:
/***************************************************************************** /*****************************************************************************
* *
* QUERY: * QUERY:
* EXPLAIN query * EXPLAIN [ANALYZE] [VERBOSE] query
* EXPLAIN ANALYZE query
* *
*****************************************************************************/ *****************************************************************************/
ExplainStmt: ExplainStmt:
EXPLAIN opt_verbose OptimizableStmt EXPLAIN opt_analyze opt_verbose OptimizableStmt
{ {
ExplainStmt *n = makeNode(ExplainStmt); ExplainStmt *n = makeNode(ExplainStmt);
n->verbose = $2; n->analyze = $2;
n->analyze = FALSE; n->verbose = $3;
n->query = (Query*)$3; n->query = (Query*)$4;
$$ = (Node *)n; $$ = (Node *)n;
} }
| EXPLAIN analyze_keyword opt_verbose OptimizableStmt | EXPLAIN opt_analyze opt_verbose ExecuteStmt
{ {
ExplainStmt *n = makeNode(ExplainStmt); ExplainStmt *n = makeNode(ExplainStmt);
n->analyze = $2;
n->verbose = $3; n->verbose = $3;
n->analyze = TRUE;
n->query = (Query*)$4; n->query = (Query*)$4;
$$ = (Node *)n; $$ = (Node *)n;
} }
; ;
opt_analyze:
analyze_keyword { $$ = TRUE; }
| /* EMPTY */ { $$ = FALSE; }
;
/***************************************************************************** /*****************************************************************************
* *
* QUERY: * QUERY:
......
...@@ -6,16 +6,20 @@ ...@@ -6,16 +6,20 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, 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
* *
* $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 #ifndef EXPLAIN_H
#define EXPLAIN_H #define EXPLAIN_H
#include "executor/executor.h"
#include "nodes/parsenodes.h" #include "nodes/parsenodes.h"
#include "tcop/dest.h" #include "tcop/dest.h"
extern void ExplainQuery(ExplainStmt *stmt, CommandDest dest); extern void ExplainQuery(ExplainStmt *stmt, CommandDest dest);
extern void ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
TupOutputState *tstate);
#endif /* EXPLAIN_H */ #endif /* EXPLAIN_H */
...@@ -6,24 +6,22 @@ ...@@ -6,24 +6,22 @@
* *
* Copyright (c) 2002, PostgreSQL Global Development Group * 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 #ifndef PREPARE_H
#define PREPARE_H #define PREPARE_H
#include "executor/executor.h"
#include "nodes/parsenodes.h" #include "nodes/parsenodes.h"
#include "tcop/dest.h" #include "tcop/dest.h"
extern void PrepareQuery(PrepareStmt *stmt); extern void PrepareQuery(PrepareStmt *stmt);
extern void ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest); extern void ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest);
extern void DeallocateQuery(DeallocateStmt *stmt); extern void DeallocateQuery(DeallocateStmt *stmt);
extern List *FetchQueryParams(const char *plan_name); extern List *FetchQueryParams(const char *plan_name);
extern void ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate);
#endif /* PREPARE_H */ #endif /* PREPARE_H */
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