Implement EXPLAIN EXECUTE. By Neil Conway, with some kibitzing from
Tom Lane.
This commit is contained in:
parent
6adb475f77
commit
c7bceca156
@ -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
|
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>
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -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
|
|||||||
<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
|
|||||||
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
|
|||||||
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 < 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 > $1 AND id < $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)
|
||||||
|
-> 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 > $1) AND (id < $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>
|
||||||
|
@ -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
|
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>
|
||||||
|
@ -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">
|
<appendix id="release">
|
||||||
@ -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
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
/*
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
* 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,7 +84,10 @@ 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 */
|
||||||
|
if (query->utilityStmt && IsA(query->utilityStmt, ExecuteStmt))
|
||||||
|
ExplainExecuteQuery(stmt, tstate);
|
||||||
|
else
|
||||||
do_text_output_oneline(tstate, "Utility statements have no plan structure");
|
do_text_output_oneline(tstate, "Utility statements have no plan structure");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
* 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"
|
||||||
@ -49,7 +50,8 @@ 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)
|
|||||||
*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)
|
|||||||
|
|
||||||
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)
|
|||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (estate)
|
||||||
FreeExecutorState(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)
|
|||||||
* 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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
/*
|
/*
|
||||||
* 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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);
|
|||||||
%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,28 +3953,32 @@ 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);
|
|
||||||
n->verbose = $2;
|
|
||||||
n->analyze = FALSE;
|
|
||||||
n->query = (Query*)$3;
|
|
||||||
$$ = (Node *)n;
|
|
||||||
}
|
|
||||||
| EXPLAIN analyze_keyword opt_verbose OptimizableStmt
|
|
||||||
{
|
{
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
| EXPLAIN opt_analyze opt_verbose ExecuteStmt
|
||||||
|
{
|
||||||
|
ExplainStmt *n = makeNode(ExplainStmt);
|
||||||
|
n->analyze = $2;
|
||||||
|
n->verbose = $3;
|
||||||
|
n->query = (Query*)$4;
|
||||||
|
$$ = (Node *)n;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
opt_analyze:
|
||||||
|
analyze_keyword { $$ = TRUE; }
|
||||||
|
| /* EMPTY */ { $$ = FALSE; }
|
||||||
;
|
;
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
|
@ -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 @@
|
|||||||
*
|
*
|
||||||
* 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 */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user