From 92cb3f5da4157085933ea5c6dcb6f560fad66626 Mon Sep 17 00:00:00 2001
From: David Fetter <david@fetter.org>
Date: Tue, 14 May 2019 22:46:44 -0700
Subject: [PATCH v4 1/8] Changed EXPLAIN ANALYZE to EXPLAIN EXEC
To: hackers
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------2.21.0"

This is a multi-part message in MIME format.
--------------2.21.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit


- In passing, made some of the defaults for EXPLAIN more helpful

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index a6c6de78f1..95e8f20e5d 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -147,6 +147,7 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
 	TupOutputState *tstate;
 	List	   *rewritten;
 	ListCell   *lc;
+	bool		buffers_set = false;
 	bool		timing_set = false;
 	bool		summary_set = false;
 
@@ -155,14 +156,17 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
 	{
 		DefElem    *opt = (DefElem *) lfirst(lc);
 
-		if (strcmp(opt->defname, "analyze") == 0)
-			es->analyze = defGetBoolean(opt);
+		if (strcmp(opt->defname, "exec") == 0)
+			es->exec = 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 if (strcmp(opt->defname, "buffers") == 0)
+		{
+			buffers_set = true;
 			es->buffers = defGetBoolean(opt);
+		}
 		else if (strcmp(opt->defname, "settings") == 0)
 			es->settings = defGetBoolean(opt);
 		else if (strcmp(opt->defname, "timing") == 0)
@@ -202,22 +206,21 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
 					 parser_errposition(pstate, opt->location)));
 	}
 
-	if (es->buffers && !es->analyze)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("EXPLAIN option BUFFERS requires ANALYZE")));
+	/* Turn buffers off automatically when exec isn't set */
+	if (!es->exec)
+		es->buffers = false;
 
 	/* if the timing was not set explicitly, set default value */
-	es->timing = (timing_set) ? es->timing : es->analyze;
+	es->timing = (timing_set) ? es->timing : es->exec;
 
-	/* check that timing is used with EXPLAIN ANALYZE */
-	if (es->timing && !es->analyze)
+	/* check that timing is used with EXPLAIN EXEC */
+	if (es->timing && !es->exec)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("EXPLAIN option TIMING requires ANALYZE")));
+				 errmsg("EXPLAIN option TIMING requires EXEC")));
 
 	/* if the summary was not set explicitly, set default value */
-	es->summary = (summary_set) ? es->summary : es->analyze;
+	es->summary = (summary_set) ? es->summary : es->exec;
 
 	/*
 	 * Parse analysis was done already, but we still have to run the rule
@@ -286,8 +289,11 @@ NewExplainState(void)
 {
 	ExplainState *es = (ExplainState *) palloc0(sizeof(ExplainState));
 
-	/* Set default options (most fields can be left as zeroes). */
+	/* Set default options (fields can be left as zeroes). */
 	es->costs = true;
+	es->buffers = true;
+	es->settings = true;
+	es->timing = true;
 	/* Prepare output buffer. */
 	es->str = makeStringInfo();
 
@@ -415,7 +421,7 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
 		/*
 		 * Likewise for DECLARE CURSOR.
 		 *
-		 * Notice that if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll
+		 * Notice that if you say EXPLAIN EXEC DECLARE CURSOR then we'll
 		 * actually run the query.  This is different from pre-8.3 behavior
 		 * but seems more useful than not running the query.  No cursor will
 		 * be created, however.
@@ -475,9 +481,9 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
 
 	Assert(plannedstmt->commandType != CMD_UTILITY);
 
-	if (es->analyze && es->timing)
+	if (es->exec && es->timing)
 		instrument_option |= INSTRUMENT_TIMER;
-	else if (es->analyze)
+	else if (es->exec)
 		instrument_option |= INSTRUMENT_ROWS;
 
 	if (es->buffers)
@@ -512,7 +518,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
 								dest, params, queryEnv, instrument_option);
 
 	/* Select execution options */
-	if (es->analyze)
+	if (es->exec)
 		eflags = 0;				/* default run-to-completion flags */
 	else
 		eflags = EXEC_FLAG_EXPLAIN_ONLY;
@@ -523,11 +529,11 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
 	ExecutorStart(queryDesc, eflags);
 
 	/* Execute the plan for statistics if asked for */
-	if (es->analyze)
+	if (es->exec)
 	{
 		ScanDirection dir;
 
-		/* EXPLAIN ANALYZE CREATE TABLE AS WITH NO DATA is weird */
+		/* EXPLAIN EXEC CREATE TABLE AS WITH NO DATA is weird */
 		if (into && into->skipData)
 			dir = NoMovementScanDirection;
 		else
@@ -556,7 +562,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
 	}
 
 	/* Print info about runtime of triggers */
-	if (es->analyze)
+	if (es->exec)
 		ExplainPrintTriggers(es, queryDesc);
 
 	/*
@@ -581,7 +587,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
 	PopActiveSnapshot();
 
 	/* We need a CCI just in case query expanded to multiple plans */
-	if (es->analyze)
+	if (es->exec)
 		CommandCounterIncrement();
 
 	totaltime += elapsed_time(&starttime);
@@ -592,7 +598,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
 	 * user can set SUMMARY OFF to not have the timing information included in
 	 * the output).  By default, ANALYZE sets SUMMARY to true.
 	 */
-	if (es->summary && es->analyze)
+	if (es->summary && es->exec)
 		ExplainPropertyFloat("Execution Time", "ms", 1000.0 * totaltime, 3,
 							 es);
 
@@ -834,7 +840,7 @@ ExplainPrintJIT(ExplainState *es, int jit_flags,
 						 "Expressions", jit_flags & PGJIT_EXPR ? "true" : "false",
 						 "Deforming", jit_flags & PGJIT_DEFORM ? "true" : "false");
 
-		if (es->analyze && es->timing)
+		if (es->exec && es->timing)
 		{
 			appendStringInfoSpaces(es->str, es->indent * 2);
 			appendStringInfo(es->str,
@@ -860,7 +866,7 @@ ExplainPrintJIT(ExplainState *es, int jit_flags,
 		ExplainPropertyBool("Deforming", jit_flags & PGJIT_DEFORM, es);
 		ExplainCloseGroup("Options", "Options", true, es);
 
-		if (es->analyze && es->timing)
+		if (es->exec && es->timing)
 		{
 			ExplainOpenGroup("Timing", "Timing", true, es);
 
@@ -1505,7 +1511,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	if (planstate->instrument)
 		InstrEndLoop(planstate->instrument);
 
-	if (es->analyze &&
+	if (es->exec &&
 		planstate->instrument && planstate->instrument->nloops > 0)
 	{
 		double		nloops = planstate->instrument->nloops;
@@ -1537,7 +1543,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
 		}
 	}
-	else if (es->analyze)
+	else if (es->exec)
 	{
 		if (es->format == EXPLAIN_FORMAT_TEXT)
 			appendStringInfoString(es->str, " (never executed)");
@@ -1606,7 +1612,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			if (plan->qual)
 				show_instrumentation_count("Rows Removed by Filter", 1,
 										   planstate, es);
-			if (es->analyze)
+			if (es->exec)
 				ExplainPropertyFloat("Heap Fetches", NULL,
 									 planstate->instrument->ntuples2, 0, es);
 			break;
@@ -1624,7 +1630,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			if (plan->qual)
 				show_instrumentation_count("Rows Removed by Filter", 1,
 										   planstate, es);
-			if (es->analyze)
+			if (es->exec)
 				show_tidbitmap_info((BitmapHeapScanState *) planstate, es);
 			break;
 		case T_SampleScan:
@@ -1658,7 +1664,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 				if (gather->initParam)
 					show_eval_params(gather->initParam, es);
 
-				if (es->analyze)
+				if (es->exec)
 				{
 					int			nworkers;
 
@@ -1704,7 +1710,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 				if (gm->initParam)
 					show_eval_params(gm->initParam, es);
 
-				if (es->analyze)
+				if (es->exec)
 				{
 					int			nworkers;
 
@@ -1869,7 +1875,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		show_buffer_usage(es, &planstate->instrument->bufusage);
 
 	/* Show worker detail */
-	if (es->analyze && es->verbose && planstate->worker_instrument)
+	if (es->exec && es->verbose && planstate->worker_instrument)
 	{
 		WorkerInstrumentation *w = planstate->worker_instrument;
 		bool		opened_group = false;
@@ -2531,12 +2537,12 @@ show_tablesample(TableSampleClause *tsc, PlanState *planstate,
 }
 
 /*
- * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
+ * If it's EXPLAIN EXEC, show tuplesort stats for a sort node
  */
 static void
 show_sort_info(SortState *sortstate, ExplainState *es)
 {
-	if (!es->analyze)
+	if (!es->exec)
 		return;
 
 	if (sortstate->sort_Done && sortstate->tuplesortstate != NULL)
@@ -2716,7 +2722,7 @@ show_hash_info(HashState *hashstate, ExplainState *es)
 }
 
 /*
- * If it's EXPLAIN ANALYZE, show exact/lossy pages for a BitmapHeapScan node
+ * If it's EXPLAIN EXEC, show exact/lossy pages for a BitmapHeapScan node
  */
 static void
 show_tidbitmap_info(BitmapHeapScanState *planstate, ExplainState *es)
@@ -2744,7 +2750,7 @@ show_tidbitmap_info(BitmapHeapScanState *planstate, ExplainState *es)
 }
 
 /*
- * If it's EXPLAIN ANALYZE, show instrumentation information for a plan node
+ * If it's EXPLAIN EXEC, show instrumentation information for a plan node
  *
  * "which" identifies which instrumentation counter to print
  */
@@ -2755,7 +2761,7 @@ show_instrumentation_count(const char *qlabel, int which,
 	double		nfiltered;
 	double		nloops;
 
-	if (!es->analyze || !planstate->instrument)
+	if (!es->exec || !planstate->instrument)
 		return;
 
 	if (which == 2)
@@ -3283,8 +3289,8 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
 			show_instrumentation_count("Rows Removed by Conflict Filter", 1, &mtstate->ps, es);
 		}
 
-		/* EXPLAIN ANALYZE display of actual outcome for each tuple proposed */
-		if (es->analyze && mtstate->ps.instrument)
+		/* EXPLAIN EXEC display of actual outcome for each tuple proposed */
+		if (es->exec && mtstate->ps.instrument)
 		{
 			double		total;
 			double		insert_path;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3dc0e8a4fb..3a7d33502c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10632,7 +10632,7 @@ opt_vacuum_relation_list:
 /*****************************************************************************
  *
  *		QUERY:
- *				EXPLAIN [ANALYZE] [VERBOSE] query
+ *				EXPLAIN [EXEC] [VERBOSE] query
  *				EXPLAIN ( options ) query
  *
  *****************************************************************************/
@@ -10645,11 +10645,11 @@ ExplainStmt:
 					n->options = NIL;
 					$$ = (Node *) n;
 				}
-		| EXPLAIN analyze_keyword opt_verbose ExplainableStmt
+		| EXPLAIN exec_keyword opt_verbose ExplainableStmt
 				{
 					ExplainStmt *n = makeNode(ExplainStmt);
 					n->query = $4;
-					n->options = list_make1(makeDefElem("analyze", NULL, @2));
+					n->options = list_make1(makeDefElem("exec", NULL, @2));
 					if ($3)
 						n->options = lappend(n->options,
 											 makeDefElem("verbose", NULL, @3));
@@ -10703,7 +10703,7 @@ explain_option_elem:
 
 explain_option_name:
 			NonReservedWord			{ $$ = $1; }
-			| analyze_keyword		{ $$ = "analyze"; }
+			| exec_keyword			{ $$ = "exec"; }
 		;
 
 explain_option_arg:
@@ -10712,6 +10712,11 @@ explain_option_arg:
 			| /* EMPTY */			{ $$ = NULL; }
 		;
 
+exec_keyword:
+			ANALYZE /* Legacy */					{}
+			| ANALYSE /* British and legacy */		{}
+		;
+
 /*****************************************************************************
  *
  *		QUERY:
diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h
index db48f29501..09cc806d7e 100644
--- a/src/include/commands/explain.h
+++ b/src/include/commands/explain.h
@@ -30,8 +30,8 @@ typedef struct ExplainState
 	StringInfo	str;			/* output buffer */
 	/* options */
 	bool		verbose;		/* be verbose */
-	bool		analyze;		/* print actual times */
 	bool		costs;			/* print estimated costs */
+	bool		exec;			/* actually execute the query, measure it */
 	bool		buffers;		/* print buffer usage */
 	bool		timing;			/* print detailed node timing */
 	bool		summary;		/* print total planning and execution timing */

--------------2.21.0--


