⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 explain.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
/*------------------------------------------------------------------------- * * explain.c *	  Explain query execution plans * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994-5, Regents of the University of California * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.139.2.1 2005/11/22 18:23:07 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/genam.h"#include "access/heapam.h"#include "catalog/pg_constraint.h"#include "catalog/pg_type.h"#include "commands/explain.h"#include "commands/prepare.h"#include "commands/trigger.h"#include "executor/executor.h"#include "executor/instrument.h"#include "lib/stringinfo.h"#include "nodes/print.h"#include "optimizer/clauses.h"#include "optimizer/planner.h"#include "optimizer/var.h"#include "parser/parsetree.h"#include "rewrite/rewriteHandler.h"#include "tcop/pquery.h"#include "utils/builtins.h"#include "utils/guc.h"#include "utils/lsyscache.h"typedef struct ExplainState{	/* options */	bool		printNodes;		/* do nodeToString() too */	bool		printAnalyze;	/* print actual times */	/* other states */	List	   *rtable;			/* range table */} ExplainState;static void ExplainOneQuery(Query *query, ExplainStmt *stmt,				TupOutputState *tstate);static double elapsed_time(instr_time *starttime);static void explain_outNode(StringInfo str,				Plan *plan, PlanState *planstate,				Plan *outer_plan,				int indent, ExplainState *es);static void show_scan_qual(List *qual, const char *qlabel,			   int scanrelid, Plan *outer_plan,			   StringInfo str, int indent, ExplainState *es);static void show_upper_qual(List *qual, const char *qlabel,				const char *outer_name, int outer_varno, Plan *outer_plan,				const char *inner_name, int inner_varno, Plan *inner_plan,				StringInfo str, int indent, ExplainState *es);static void show_sort_keys(List *tlist, int nkeys, AttrNumber *keycols,			   const char *qlabel,			   StringInfo str, int indent, ExplainState *es);/* * ExplainQuery - *	  execute an EXPLAIN command */voidExplainQuery(ExplainStmt *stmt, DestReceiver *dest){	Query	   *query = stmt->query;	TupOutputState *tstate;	List	   *rewritten;	ListCell   *l;	/*	 * Because the planner is not cool about not scribbling on its input, we	 * make a preliminary copy of the source querytree.  This prevents	 * problems in the case that the EXPLAIN is in a portal or plpgsql	 * function and is executed repeatedly.  (See also the same hack in	 * DECLARE CURSOR and PREPARE.)  XXX the planner really shouldn't modify	 * its input ... FIXME someday.	 */	query = copyObject(query);	/* prepare for projection of tuples */	tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt));	if (query->commandType == CMD_UTILITY)	{		/* Rewriter will not cope with utility statements */		if (query->utilityStmt && IsA(query->utilityStmt, DeclareCursorStmt))			ExplainOneQuery(query, stmt, tstate);		else if (query->utilityStmt && IsA(query->utilityStmt, ExecuteStmt))			ExplainExecuteQuery(stmt, tstate);		else			do_text_output_oneline(tstate, "Utility statements have no plan structure");	}	else	{		/*		 * Must acquire locks in case we didn't come fresh from the parser.		 * XXX this also scribbles on query, another reason for copyObject		 */		AcquireRewriteLocks(query);		/* Rewrite through rule system */		rewritten = QueryRewrite(query);		if (rewritten == NIL)		{			/* In the case of an INSTEAD NOTHING, tell at least that */			do_text_output_oneline(tstate, "Query rewrites to nothing");		}		else		{			/* Explain every plan */			foreach(l, rewritten)			{				ExplainOneQuery(lfirst(l), stmt, tstate);				/* put a blank line between plans */				if (lnext(l) != NULL)					do_text_output_oneline(tstate, "");			}		}	}	end_tup_output(tstate);}/* * ExplainResultDesc - *	  construct the result tupledesc for an EXPLAIN */TupleDescExplainResultDesc(ExplainStmt *stmt){	TupleDesc	tupdesc;	/* need a tuple descriptor representing a single TEXT column */	tupdesc = CreateTemplateTupleDesc(1, false);	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",					   TEXTOID, -1, 0);	return tupdesc;}/* * ExplainOneQuery - *	  print out the execution plan for one query */static voidExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate){	Plan	   *plan;	QueryDesc  *queryDesc;	bool		isCursor = false;	int			cursorOptions = 0;	/* planner will not cope with utility statements */	if (query->commandType == CMD_UTILITY)	{		if (query->utilityStmt && IsA(query->utilityStmt, DeclareCursorStmt))		{			DeclareCursorStmt *dcstmt;			List	   *rewritten;			dcstmt = (DeclareCursorStmt *) query->utilityStmt;			query = (Query *) dcstmt->query;			isCursor = true;			cursorOptions = dcstmt->options;			/* Still need to rewrite cursor command */			Assert(query->commandType == CMD_SELECT);			/* get locks (we assume ExplainQuery already copied tree) */			AcquireRewriteLocks(query);			rewritten = QueryRewrite(query);			if (list_length(rewritten) != 1)				elog(ERROR, "unexpected rewrite result");			query = (Query *) linitial(rewritten);			Assert(query->commandType == CMD_SELECT);			/* do not actually execute the underlying query! */			stmt->analyze = false;		}		else if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))		{			do_text_output_oneline(tstate, "NOTIFY");			return;		}		else		{			do_text_output_oneline(tstate, "UTILITY");			return;		}	}	/* plan the query */	plan = planner(query, isCursor, cursorOptions, NULL);	/*	 * Update snapshot command ID to ensure this query sees results of any	 * previously executed queries.  (It's a bit cheesy to modify	 * ActiveSnapshot without making a copy, but for the limited ways in which	 * EXPLAIN can be invoked, I think it's OK, because the active snapshot	 * shouldn't be shared with anything else anyway.)	 */	ActiveSnapshot->curcid = GetCurrentCommandId();	/* Create a QueryDesc requesting no output */	queryDesc = CreateQueryDesc(query, plan,								ActiveSnapshot, InvalidSnapshot,								None_Receiver, 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 */voidExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,			   TupOutputState *tstate){	instr_time	starttime;	double		totaltime = 0;	ExplainState *es;	StringInfo	str;	INSTR_TIME_SET_CURRENT(starttime);	/* If analyzing, we need to cope with queued triggers */	if (stmt->analyze)		AfterTriggerBeginQuery();	/* call ExecutorStart to prepare the plan for execution */	ExecutorStart(queryDesc, !stmt->analyze);	/* Execute the plan for statistics if asked for */	if (stmt->analyze)	{		/* run the plan */		ExecutorRun(queryDesc, ForwardScanDirection, 0L);		/* We can't clean up 'till we're done printing the stats... */		totaltime += elapsed_time(&starttime);	}	es = (ExplainState *) palloc0(sizeof(ExplainState));	es->printNodes = stmt->verbose;	es->printAnalyze = stmt->analyze;	es->rtable = queryDesc->parsetree->rtable;	if (es->printNodes)	{		char	   *s;		char	   *f;		s = nodeToString(queryDesc->plantree);		if (s)		{			if (Explain_pretty_print)				f = pretty_format_node_dump(s);			else				f = format_node_dump(s);			pfree(s);			do_text_output_multiline(tstate, f);			pfree(f);			do_text_output_oneline(tstate, ""); /* separator line */		}	}	str = makeStringInfo();	explain_outNode(str, queryDesc->plantree, queryDesc->planstate,					NULL, 0, es);	/*	 * If we ran the command, run any AFTER triggers it queued.  (Note this	 * will not include DEFERRED triggers; since those don't run until end of	 * transaction, we can't measure them.)  Include into total runtime.	 */	if (stmt->analyze)	{		INSTR_TIME_SET_CURRENT(starttime);		AfterTriggerEndQuery(queryDesc->estate);		totaltime += elapsed_time(&starttime);	}	/* Print info about runtime of triggers */	if (es->printAnalyze)	{		ResultRelInfo *rInfo;		int			numrels = queryDesc->estate->es_num_result_relations;		int			nr;		rInfo = queryDesc->estate->es_result_relations;		for (nr = 0; nr < numrels; rInfo++, nr++)		{			int			nt;			if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)				continue;			for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)			{				Trigger    *trig = rInfo->ri_TrigDesc->triggers + nt;				Instrumentation *instr = rInfo->ri_TrigInstrument + nt;				char	   *conname;				/* Must clean up instrumentation state */				InstrEndLoop(instr);				/*				 * We ignore triggers that were never invoked; they likely				 * aren't relevant to the current query type.				 */				if (instr->ntuples == 0)					continue;				if (trig->tgisconstraint &&				(conname = GetConstraintNameForTrigger(trig->tgoid)) != NULL)				{					appendStringInfo(str, "Trigger for constraint %s",									 conname);					pfree(conname);				}				else					appendStringInfo(str, "Trigger %s", trig->tgname);				if (numrels > 1)					appendStringInfo(str, " on %s",							RelationGetRelationName(rInfo->ri_RelationDesc));				appendStringInfo(str, ": time=%.3f calls=%.0f\n",								 1000.0 * instr->total,								 instr->ntuples);			}		}	}	/*	 * Close down the query and free resources.  Include time for this in the	 * total runtime (although it should be pretty minimal).	 */	INSTR_TIME_SET_CURRENT(starttime);	ExecutorEnd(queryDesc);	FreeQueryDesc(queryDesc);	/* We need a CCI just in case query expanded to multiple plans */	if (stmt->analyze)		CommandCounterIncrement();	totaltime += elapsed_time(&starttime);	if (stmt->analyze)		appendStringInfo(str, "Total runtime: %.3f ms\n",						 1000.0 * totaltime);	do_text_output_multiline(tstate, str->data);	pfree(str->data);	pfree(str);	pfree(es);}/* Compute elapsed time in seconds since given timestamp */static doubleelapsed_time(instr_time *starttime){	instr_time	endtime;	INSTR_TIME_SET_CURRENT(endtime);#ifndef WIN32	endtime.tv_sec -= starttime->tv_sec;	endtime.tv_usec -= starttime->tv_usec;	while (endtime.tv_usec < 0)	{		endtime.tv_usec += 1000000;		endtime.tv_sec--;	}#else							/* WIN32 */	endtime.QuadPart -= starttime->QuadPart;#endif	return INSTR_TIME_GET_DOUBLE(endtime);}/* * explain_outNode - *	  converts a Plan node into ascii string and appends it to 'str' * * planstate points to the executor state node corresponding to the plan node. * We need this to get at the instrumentation data (if any) as well as the * list of subplans. * * outer_plan, if not null, references another plan node that is the outer * side of a join with the current node.  This is only interesting for * deciphering runtime keys of an inner indexscan. */static voidexplain_outNode(StringInfo str,				Plan *plan, PlanState *planstate,				Plan *outer_plan,				int indent, ExplainState *es){	char	   *pname;	int			i;	if (plan == NULL)	{		appendStringInfoChar(str, '\n');		return;	}	switch (nodeTag(plan))	{		case T_Result:			pname = "Result";			break;		case T_Append:			pname = "Append";			break;		case T_BitmapAnd:			pname = "BitmapAnd";			break;		case T_BitmapOr:			pname = "BitmapOr";			break;		case T_NestLoop:			switch (((NestLoop *) plan)->join.jointype)			{				case JOIN_INNER:					pname = "Nested Loop";					break;				case JOIN_LEFT:					pname = "Nested Loop Left Join";					break;				case JOIN_FULL:					pname = "Nested Loop Full Join";					break;				case JOIN_RIGHT:					pname = "Nested Loop Right Join";					break;				case JOIN_IN:					pname = "Nested Loop IN Join";					break;				default:					pname = "Nested Loop ??? Join";					break;			}			break;		case T_MergeJoin:			switch (((MergeJoin *) plan)->join.jointype)			{				case JOIN_INNER:					pname = "Merge Join";					break;				case JOIN_LEFT:					pname = "Merge Left Join";					break;				case JOIN_FULL:					pname = "Merge Full Join";					break;				case JOIN_RIGHT:					pname = "Merge Right Join";					break;				case JOIN_IN:					pname = "Merge IN Join";					break;				default:					pname = "Merge ??? Join";					break;			}			break;		case T_HashJoin:			switch (((HashJoin *) plan)->join.jointype)			{				case JOIN_INNER:					pname = "Hash Join";					break;				case JOIN_LEFT:					pname = "Hash Left Join";					break;				case JOIN_FULL:					pname = "Hash Full Join";					break;				case JOIN_RIGHT:					pname = "Hash Right Join";					break;				case JOIN_IN:					pname = "Hash IN Join";					break;				default:					pname = "Hash ??? Join";					break;			}			break;		case T_SeqScan:			pname = "Seq Scan";			break;		case T_IndexScan:			pname = "Index Scan";			break;		case T_BitmapIndexScan:			pname = "Bitmap Index Scan";			break;		case T_BitmapHeapScan:			pname = "Bitmap Heap Scan";			break;		case T_TidScan:			pname = "Tid Scan";			break;		case T_SubqueryScan:			pname = "Subquery Scan";			break;		case T_FunctionScan:			pname = "Function Scan";			break;		case T_Material:			pname = "Materialize";			break;		case T_Sort:			pname = "Sort";			break;		case T_Group:			pname = "Group";			break;		case T_Agg:			switch (((Agg *) plan)->aggstrategy)			{				case AGG_PLAIN:					pname = "Aggregate";					break;				case AGG_SORTED:					pname = "GroupAggregate";					break;				case AGG_HASHED:					pname = "HashAggregate";					break;				default:					pname = "Aggregate ???";					break;			}			break;		case T_Unique:			pname = "Unique";			break;		case T_SetOp:			switch (((SetOp *) plan)->cmd)			{				case SETOPCMD_INTERSECT:					pname = "SetOp Intersect";					break;				case SETOPCMD_INTERSECT_ALL:					pname = "SetOp Intersect All";					break;				case SETOPCMD_EXCEPT:					pname = "SetOp Except";					break;				case SETOPCMD_EXCEPT_ALL:					pname = "SetOp Except All";					break;				default:					pname = "SetOp ???";					break;			}			break;		case T_Limit:			pname = "Limit";			break;		case T_Hash:			pname = "Hash";			break;		default:			pname = "???";			break;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -