@ -7,7 +7,7 @@
* Portions Copyright ( c ) 1994 - 5 , Regents of the University of California
* Portions Copyright ( c ) 1994 - 5 , Regents of the University of California
*
*
* IDENTIFICATION
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / backend / commands / explain . c , v 1.187 2009 / 07 / 24 21 : 08 : 42 tgl Exp $
* $ PostgreSQL : pgsql / src / backend / commands / explain . c , v 1.188 2009 / 07 / 26 23 : 34 : 17 tgl Exp $
*
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
*/
@ -16,6 +16,7 @@
# include "access/xact.h"
# include "access/xact.h"
# include "catalog/pg_constraint.h"
# include "catalog/pg_constraint.h"
# include "catalog/pg_type.h"
# include "catalog/pg_type.h"
# include "commands/defrem.h"
# include "commands/explain.h"
# include "commands/explain.h"
# include "commands/prepare.h"
# include "commands/prepare.h"
# include "commands/trigger.h"
# include "commands/trigger.h"
@ -40,20 +41,8 @@ ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL;
explain_get_index_name_hook_type explain_get_index_name_hook = NULL ;
explain_get_index_name_hook_type explain_get_index_name_hook = NULL ;
typedef struct ExplainState
static void ExplainOneQuery ( Query * query , ExplainState * es ,
{
const char * queryString , ParamListInfo params ) ;
StringInfo str ; /* output buffer */
/* options */
bool printTList ; /* print plan targetlists */
bool printAnalyze ; /* print actual times */
/* other states */
PlannedStmt * pstmt ; /* top of plan */
List * rtable ; /* range table */
} ExplainState ;
static void ExplainOneQuery ( Query * query , ExplainStmt * stmt ,
const char * queryString ,
ParamListInfo params , TupOutputState * tstate ) ;
static void report_triggers ( ResultRelInfo * rInfo , bool show_relname ,
static void report_triggers ( ResultRelInfo * rInfo , bool show_relname ,
StringInfo buf ) ;
StringInfo buf ) ;
static double elapsed_time ( instr_time * starttime ) ;
static double elapsed_time ( instr_time * starttime ) ;
@ -84,11 +73,33 @@ void
ExplainQuery ( ExplainStmt * stmt , const char * queryString ,
ExplainQuery ( ExplainStmt * stmt , const char * queryString ,
ParamListInfo params , DestReceiver * dest )
ParamListInfo params , DestReceiver * dest )
{
{
ExplainState es ;
Oid * param_types ;
Oid * param_types ;
int num_params ;
int num_params ;
TupOutputState * tstate ;
TupOutputState * tstate ;
List * rewritten ;
List * rewritten ;
ListCell * l ;
ListCell * lc ;
/* Initialize ExplainState. */
ExplainInitState ( & es ) ;
/* Parse options list. */
foreach ( lc , stmt - > options )
{
DefElem * opt = ( DefElem * ) lfirst ( lc ) ;
if ( strcmp ( opt - > defname , " analyze " ) = = 0 )
es . analyze = defGetBoolean ( opt ) ;
else if ( strcmp ( opt - > defname , " verbose " ) = = 0 )
es . verbose = defGetBoolean ( opt ) ;
else if ( strcmp ( opt - > defname , " costs " ) = = 0 )
es . costs = defGetBoolean ( opt ) ;
else
ereport ( ERROR ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
errmsg ( " unrecognized EXPLAIN option \" %s \" " ,
opt - > defname ) ) ) ;
}
/* Convert parameter type data to the form parser wants */
/* Convert parameter type data to the form parser wants */
getParamListTypes ( params , & param_types , & num_params ) ;
getParamListTypes ( params , & param_types , & num_params ) ;
@ -106,28 +117,44 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
rewritten = pg_analyze_and_rewrite ( ( Node * ) copyObject ( stmt - > query ) ,
rewritten = pg_analyze_and_rewrite ( ( Node * ) copyObject ( stmt - > query ) ,
queryString , param_types , num_params ) ;
queryString , param_types , num_params ) ;
/* prepare for projection of tuples */
tstate = begin_tup_output_tupdesc ( dest , ExplainResultDesc ( stmt ) ) ;
if ( rewritten = = NIL )
if ( rewritten = = NIL )
{
{
/* In the case of an INSTEAD NOTHING, tell at least that */
/* In the case of an INSTEAD NOTHING, tell at least that */
do_text_output_oneline ( tstate , " Query rewrites to nothing " ) ;
appendStringInfoString ( es . str , " Query rewrites to nothing \n " ) ;
}
}
else
else
{
{
ListCell * l ;
/* Explain every plan */
/* Explain every plan */
foreach ( l , rewritten )
foreach ( l , rewritten )
{
{
ExplainOneQuery ( ( Query * ) lfirst ( l ) , stmt ,
ExplainOneQuery ( ( Query * ) lfirst ( l ) , & es , queryString , params ) ;
queryString , params , tstate ) ;
/* put a blank line between plans */
/* put a blank line between plans */
if ( lnext ( l ) ! = NULL )
if ( lnext ( l ) ! = NULL )
do_text_output_oneline ( tstate , " " ) ;
appendStringInfoChar ( es . str , ' \n ' ) ;
}
}
}
}
/* output tuples */
tstate = begin_tup_output_tupdesc ( dest , ExplainResultDesc ( stmt ) ) ;
do_text_output_multiline ( tstate , es . str - > data ) ;
end_tup_output ( tstate ) ;
end_tup_output ( tstate ) ;
pfree ( es . str - > data ) ;
}
/*
* Initialize ExplainState .
*/
void
ExplainInitState ( ExplainState * es )
{
/* Set default options. */
memset ( es , 0 , sizeof ( ExplainState ) ) ;
es - > costs = true ;
/* Prepare output buffer. */
es - > str = makeStringInfo ( ) ;
}
}
/*
/*
@ -151,20 +178,19 @@ ExplainResultDesc(ExplainStmt *stmt)
* print out the execution plan for one Query
* print out the execution plan for one Query
*/
*/
static void
static void
ExplainOneQuery ( Query * query , ExplainStmt * stmt , const char * queryString ,
ExplainOneQuery ( Query * query , ExplainState * es ,
ParamListInfo params , TupOutputState * tstate )
const char * queryString , ParamListInfo params )
{
{
/* planner will not cope with utility statements */
/* planner will not cope with utility statements */
if ( query - > commandType = = CMD_UTILITY )
if ( query - > commandType = = CMD_UTILITY )
{
{
ExplainOneUtility ( query - > utilityStmt , stmt ,
ExplainOneUtility ( query - > utilityStmt , es , queryString , params ) ;
queryString , params , tstate ) ;
return ;
return ;
}
}
/* if an advisor plugin is present, let it manage things */
/* if an advisor plugin is present, let it manage things */
if ( ExplainOneQuery_hook )
if ( ExplainOneQuery_hook )
( * ExplainOneQuery_hook ) ( query , stmt , queryString , params , tstate ) ;
( * ExplainOneQuery_hook ) ( query , e s, queryString , params ) ;
else
else
{
{
PlannedStmt * plan ;
PlannedStmt * plan ;
@ -173,7 +199,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, const char *queryString,
plan = pg_plan_query ( query , 0 , params ) ;
plan = pg_plan_query ( query , 0 , params ) ;
/* run it (if needed) and produce output */
/* run it (if needed) and produce output */
ExplainOnePlan ( plan , stmt , queryString , params , tstate ) ;
ExplainOnePlan ( plan , e s, queryString , params ) ;
}
}
}
}
@ -187,21 +213,20 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, const char *queryString,
* EXPLAIN EXECUTE case
* EXPLAIN EXECUTE case
*/
*/
void
void
ExplainOneUtility ( Node * utilityStmt , ExplainStmt * stmt ,
ExplainOneUtility ( Node * utilityStmt , ExplainState * es ,
const char * queryString , ParamListInfo params ,
const char * queryString , ParamListInfo params )
TupOutputState * tstate )
{
{
if ( utilityStmt = = NULL )
if ( utilityStmt = = NULL )
return ;
return ;
if ( IsA ( utilityStmt , ExecuteStmt ) )
if ( IsA ( utilityStmt , ExecuteStmt ) )
ExplainExecuteQuery ( ( ExecuteStmt * ) utilityStmt , stmt ,
ExplainExecuteQuery ( ( ExecuteStmt * ) utilityStmt , e s,
queryString , params , tstate ) ;
queryString , params ) ;
else if ( IsA ( utilityStmt , NotifyStmt ) )
else if ( IsA ( utilityStmt , NotifyStmt ) )
do_text_output_oneline ( tstate , " NOTIFY " ) ;
appendStringInfoString ( es - > str , " NOTIFY \n " ) ;
else
else
do_text_output_oneline ( tstate ,
appendStringInfoString ( es - > str ,
" Utility statements have no plan structure " ) ;
" Utility statements have no plan structure \n " ) ;
}
}
/*
/*
@ -219,14 +244,12 @@ ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt,
* to call it .
* to call it .
*/
*/
void
void
ExplainOnePlan ( PlannedStmt * plannedstmt , ExplainStmt * stmt ,
ExplainOnePlan ( PlannedStmt * plannedstmt , ExplainState * es ,
const char * queryString , ParamListInfo params ,
const char * queryString , ParamListInfo params )
TupOutputState * tstate )
{
{
QueryDesc * queryDesc ;
QueryDesc * queryDesc ;
instr_time starttime ;
instr_time starttime ;
double totaltime = 0 ;
double totaltime = 0 ;
StringInfoData buf ;
int eflags ;
int eflags ;
/*
/*
@ -238,17 +261,16 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
/* Create a QueryDesc requesting no output */
/* Create a QueryDesc requesting no output */
queryDesc = CreateQueryDesc ( plannedstmt , queryString ,
queryDesc = CreateQueryDesc ( plannedstmt , queryString ,
GetActiveSnapshot ( ) , InvalidSnapshot ,
GetActiveSnapshot ( ) , InvalidSnapshot ,
None_Receiver , params ,
None_Receiver , params , es - > analyze ) ;
stmt - > analyze ) ;
INSTR_TIME_SET_CURRENT ( starttime ) ;
INSTR_TIME_SET_CURRENT ( starttime ) ;
/* If analyzing, we need to cope with queued triggers */
/* If analyzing, we need to cope with queued triggers */
if ( stmt - > analyze )
if ( e s- > analyze )
AfterTriggerBeginQuery ( ) ;
AfterTriggerBeginQuery ( ) ;
/* Select execution options */
/* Select execution options */
if ( stmt - > analyze )
if ( e s- > analyze )
eflags = 0 ; /* default run-to-completion flags */
eflags = 0 ; /* default run-to-completion flags */
else
else
eflags = EXEC_FLAG_EXPLAIN_ONLY ;
eflags = EXEC_FLAG_EXPLAIN_ONLY ;
@ -257,7 +279,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
ExecutorStart ( queryDesc , eflags ) ;
ExecutorStart ( queryDesc , eflags ) ;
/* Execute the plan for statistics if asked for */
/* Execute the plan for statistics if asked for */
if ( stmt - > analyze )
if ( e s- > analyze )
{
{
/* run the plan */
/* run the plan */
ExecutorRun ( queryDesc , ForwardScanDirection , 0L ) ;
ExecutorRun ( queryDesc , ForwardScanDirection , 0L ) ;
@ -267,15 +289,14 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
}
}
/* Create textual dump of plan tree */
/* Create textual dump of plan tree */
initStringInfo ( & buf ) ;
ExplainPrintPlan ( es , queryDesc ) ;
ExplainPrintPlan ( & buf , queryDesc , stmt - > analyze , stmt - > verbose ) ;
/*
/*
* If we ran the command , run any AFTER triggers it queued . ( Note this
* If we ran the command , run any AFTER triggers it queued . ( Note this
* will not include DEFERRED triggers ; since those don ' t run until end of
* will not include DEFERRED triggers ; since those don ' t run until end of
* transaction , we can ' t measure them . ) Include into total runtime .
* transaction , we can ' t measure them . ) Include into total runtime .
*/
*/
if ( stmt - > analyze )
if ( e s- > analyze )
{
{
INSTR_TIME_SET_CURRENT ( starttime ) ;
INSTR_TIME_SET_CURRENT ( starttime ) ;
AfterTriggerEndQuery ( queryDesc - > estate ) ;
AfterTriggerEndQuery ( queryDesc - > estate ) ;
@ -283,7 +304,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
}
}
/* Print info about runtime of triggers */
/* Print info about runtime of triggers */
if ( stmt - > analyze )
if ( e s- > analyze )
{
{
ResultRelInfo * rInfo ;
ResultRelInfo * rInfo ;
bool show_relname ;
bool show_relname ;
@ -295,12 +316,12 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
show_relname = ( numrels > 1 | | targrels ! = NIL ) ;
show_relname = ( numrels > 1 | | targrels ! = NIL ) ;
rInfo = queryDesc - > estate - > es_result_relations ;
rInfo = queryDesc - > estate - > es_result_relations ;
for ( nr = 0 ; nr < numrels ; rInfo + + , nr + + )
for ( nr = 0 ; nr < numrels ; rInfo + + , nr + + )
report_triggers ( rInfo , show_relname , & buf ) ;
report_triggers ( rInfo , show_relname , es - > str ) ;
foreach ( l , targrels )
foreach ( l , targrels )
{
{
rInfo = ( ResultRelInfo * ) lfirst ( l ) ;
rInfo = ( ResultRelInfo * ) lfirst ( l ) ;
report_triggers ( rInfo , show_relname , & buf ) ;
report_triggers ( rInfo , show_relname , es - > str ) ;
}
}
}
}
@ -317,45 +338,34 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt,
PopActiveSnapshot ( ) ;
PopActiveSnapshot ( ) ;
/* We need a CCI just in case query expanded to multiple plans */
/* We need a CCI just in case query expanded to multiple plans */
if ( stmt - > analyze )
if ( e s- > analyze )
CommandCounterIncrement ( ) ;
CommandCounterIncrement ( ) ;
totaltime + = elapsed_time ( & starttime ) ;
totaltime + = elapsed_time ( & starttime ) ;
if ( stmt - > analyze )
if ( e s- > analyze )
appendStringInfo ( & buf , " Total runtime: %.3f ms \n " ,
appendStringInfo ( es - > str , " Total runtime: %.3f ms \n " ,
1000.0 * totaltime ) ;
1000.0 * totaltime ) ;
do_text_output_multiline ( tstate , buf . data ) ;
pfree ( buf . data ) ;
}
}
/*
/*
* ExplainPrintPlan -
* ExplainPrintPlan -
* convert a QueryDesc ' s plan tree to text and append it to ' str '
* convert a QueryDesc ' s plan tree to text and append it to es - > str
*
*
* ' analyze ' means to include runtime instrumentation results
* The caller should have set up the options fields of * es , as well as
* ' verbose ' means a verbose printout ( currently , it shows targetlists )
* initializing the output buffer es - > str . Other fields in * es are
* initialized here .
*
*
* NB : will not work on utility statements
* NB : will not work on utility statements
*/
*/
void
void
ExplainPrintPlan ( StringInfo str , QueryDesc * queryDesc ,
ExplainPrintPlan ( ExplainState * es , QueryDesc * queryDesc )
bool analyze , bool verbose )
{
{
ExplainState es ;
Assert ( queryDesc - > plannedstmt ! = NULL ) ;
Assert ( queryDesc - > plannedstmt ! = NULL ) ;
es - > pstmt = queryDesc - > plannedstmt ;
memset ( & es , 0 , sizeof ( es ) ) ;
es - > rtable = queryDesc - > plannedstmt - > rtable ;
es . str = str ;
es . printTList = verbose ;
es . printAnalyze = analyze ;
es . pstmt = queryDesc - > plannedstmt ;
es . rtable = queryDesc - > plannedstmt - > rtable ;
ExplainNode ( queryDesc - > plannedstmt - > planTree , queryDesc - > planstate ,
ExplainNode ( queryDesc - > plannedstmt - > planTree , queryDesc - > planstate ,
NULL , 0 , & es ) ;
NULL , 0 , es ) ;
}
}
/*
/*
@ -692,9 +702,10 @@ ExplainNode(Plan *plan, PlanState *planstate,
break ;
break ;
}
}
appendStringInfo ( es - > str , " (cost=%.2f..%.2f rows=%.0f width=%d) " ,
if ( es - > costs )
plan - > startup_cost , plan - > total_cost ,
appendStringInfo ( es - > str , " (cost=%.2f..%.2f rows=%.0f width=%d) " ,
plan - > plan_rows , plan - > plan_width ) ;
plan - > startup_cost , plan - > total_cost ,
plan - > plan_rows , plan - > plan_width ) ;
/*
/*
* We have to forcibly clean up the instrumentation state because we
* We have to forcibly clean up the instrumentation state because we
@ -714,12 +725,12 @@ ExplainNode(Plan *plan, PlanState *planstate,
planstate - > instrument - > ntuples / nloops ,
planstate - > instrument - > ntuples / nloops ,
planstate - > instrument - > nloops ) ;
planstate - > instrument - > nloops ) ;
}
}
else if ( es - > printA nalyze)
else if ( es - > a nalyze)
appendStringInfoString ( es - > str , " (never executed) " ) ;
appendStringInfoString ( es - > str , " (never executed) " ) ;
appendStringInfoChar ( es - > str , ' \n ' ) ;
appendStringInfoChar ( es - > str , ' \n ' ) ;
/* target list */
/* target list */
if ( es - > printTList )
if ( es - > verbose )
show_plan_tlist ( plan , indent , es ) ;
show_plan_tlist ( plan , indent , es ) ;
/* quals, sort keys, etc */
/* quals, sort keys, etc */
@ -1025,7 +1036,7 @@ static void
show_sort_info ( SortState * sortstate , int indent , ExplainState * es )
show_sort_info ( SortState * sortstate , int indent , ExplainState * es )
{
{
Assert ( IsA ( sortstate , SortState ) ) ;
Assert ( IsA ( sortstate , SortState ) ) ;
if ( es - > printA nalyze & & sortstate - > sort_Done & &
if ( es - > a nalyze & & sortstate - > sort_Done & &
sortstate - > tuplesortstate ! = NULL )
sortstate - > tuplesortstate ! = NULL )
{
{
char * sortinfo ;
char * sortinfo ;