From 0802cdd382cdb11739f9054281f3ccbb975686c0 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Wed, 23 Oct 2019 17:39:09 +1300
Subject: [PATCH 2/2] Improve EXPLAIN of Sort in parallel queries.

Previously, it wasn't very clear to the uninitiated what work
a Sort node did in the leader process.  Label the ANALYZE output
explicitly, like VERBOSE does.

Author: Thomas Munro
---
 src/backend/commands/explain.c                | 134 ++++++++++--------
 src/test/regress/expected/select_parallel.out |   2 +-
 2 files changed, 75 insertions(+), 61 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index a1fb22c9f7..4247a3c749 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -2553,86 +2553,100 @@ show_tablesample(TableSampleClause *tsc, PlanState *planstate,
 	}
 }
 
-/*
- * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
- */
 static void
-show_sort_info(SortState *sortstate, ExplainState *es)
+show_sort_info_per_process(TuplesortInstrumentation *sinstrument,
+						   bool has_workers,
+						   int worker_number,
+						   bool *opened_group,
+						   ExplainState *es)
 {
-	if (!es->analyze)
-		return;
+	const char *sortMethod;
+	const char *spaceType;
+	long		spaceUsed;
 
-	if (sortstate->sort_Done && sortstate->tuplesortstate != NULL)
+	if (sinstrument->sortMethod == SORT_TYPE_STILL_IN_PROGRESS)
+		return;		/* ignore any unfilled slots */
+
+	sortMethod = tuplesort_method_name(sinstrument->sortMethod);
+	spaceType = tuplesort_space_type_name(sinstrument->spaceType);
+	spaceUsed = sinstrument->spaceUsed;
+
+	if (es->format == EXPLAIN_FORMAT_TEXT)
 	{
-		Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate;
-		TuplesortInstrumentation stats;
-		const char *sortMethod;
-		const char *spaceType;
-		long		spaceUsed;
+		char	prefix[80];
 
-		tuplesort_get_stats(state, &stats);
-		sortMethod = tuplesort_method_name(stats.sortMethod);
-		spaceType = tuplesort_space_type_name(stats.spaceType);
-		spaceUsed = stats.spaceUsed;
+		if (!has_workers)
+			snprintf(prefix, sizeof(prefix), "");
+		else if (worker_number < 0)
+			snprintf(prefix, sizeof(prefix), "Leader:  ");
+		else
+			snprintf(prefix, sizeof(prefix), "Worker %d:  ", worker_number);
 
-		if (es->format == EXPLAIN_FORMAT_TEXT)
+		appendStringInfoSpaces(es->str, es->indent * 2);
+		appendStringInfo(es->str,
+						 "%sSort Method: %s  %s: %ldkB\n",
+						 prefix, sortMethod, spaceType, spaceUsed);
+	}
+	else
+	{
+		if (!has_workers)
 		{
-			appendStringInfoSpaces(es->str, es->indent * 2);
-			appendStringInfo(es->str, "Sort Method: %s  %s: %ldkB\n",
-							 sortMethod, spaceType, spaceUsed);
+			ExplainPropertyText("Sort Method", sortMethod, es);
+			ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es);
+			ExplainPropertyText("Sort Space Type", spaceType, es);
 		}
 		else
 		{
+			if (!*opened_group)
+			{
+				ExplainOpenGroup("Workers", "Workers", false, es);
+				*opened_group = true;
+			}
+			ExplainOpenGroup("Worker", NULL, true, es);
+			ExplainPropertyInteger("Worker Number", NULL, worker_number, es);
 			ExplainPropertyText("Sort Method", sortMethod, es);
 			ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es);
 			ExplainPropertyText("Sort Space Type", spaceType, es);
+			ExplainCloseGroup("Worker", NULL, true, es);
 		}
 	}
+}
 
-	if (sortstate->shared_info != NULL)
+/*
+ * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
+ */
+static void
+show_sort_info(SortState *sortstate, ExplainState *es)
+{
+	bool		opened_group = false;
+
+	if (!es->analyze)
+		return;
+
+	/*
+	 * Show the leader stats for parallel queries, or only stats for
+	 * non-parallel queries.
+	 */
+	if (sortstate->sort_Done && sortstate->tuplesortstate != NULL)
 	{
-		int			n;
-		bool		opened_group = false;
+		Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate;
+		TuplesortInstrumentation stats;
 
-		for (n = 0; n < sortstate->shared_info->num_workers; n++)
-		{
-			TuplesortInstrumentation *sinstrument;
-			const char *sortMethod;
-			const char *spaceType;
-			long		spaceUsed;
-
-			sinstrument = &sortstate->shared_info->sinstrument[n];
-			if (sinstrument->sortMethod == SORT_TYPE_STILL_IN_PROGRESS)
-				continue;		/* ignore any unfilled slots */
-			sortMethod = tuplesort_method_name(sinstrument->sortMethod);
-			spaceType = tuplesort_space_type_name(sinstrument->spaceType);
-			spaceUsed = sinstrument->spaceUsed;
+		tuplesort_get_stats(state, &stats);
+		show_sort_info_per_process(&stats, sortstate->shared_info != NULL,
+								   -1, &opened_group, es);
+	}
 
-			if (es->format == EXPLAIN_FORMAT_TEXT)
-			{
-				appendStringInfoSpaces(es->str, es->indent * 2);
-				appendStringInfo(es->str,
-								 "Worker %d:  Sort Method: %s  %s: %ldkB\n",
-								 n, sortMethod, spaceType, spaceUsed);
-			}
-			else
-			{
-				if (!opened_group)
-				{
-					ExplainOpenGroup("Workers", "Workers", false, es);
-					opened_group = true;
-				}
-				ExplainOpenGroup("Worker", NULL, true, es);
-				ExplainPropertyInteger("Worker Number", NULL, n, es);
-				ExplainPropertyText("Sort Method", sortMethod, es);
-				ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es);
-				ExplainPropertyText("Sort Space Type", spaceType, es);
-				ExplainCloseGroup("Worker", NULL, true, es);
-			}
-		}
-		if (opened_group)
-			ExplainCloseGroup("Workers", "Workers", false, es);
+	/* Show the worker stats. */
+	if (sortstate->shared_info != NULL)
+	{
+		for (int n = 0; n < sortstate->shared_info->num_workers; n++)
+			show_sort_info_per_process(&sortstate->shared_info->sinstrument[n],
+									   true, n, &opened_group, es);
 	}
+
+	if (opened_group)
+		ExplainCloseGroup("Workers", "Workers", false, es);
 }
 
 /*
diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out
index 0eca76cb41..5c0738ccdc 100644
--- a/src/test/regress/expected/select_parallel.out
+++ b/src/test/regress/expected/select_parallel.out
@@ -551,7 +551,7 @@ select * from explain_parallel_sort_stats();
          Workers Launched: 4
          ->  Sort (actual rows=2000 loops=15)
                Sort Key: tenk1.ten
-               Sort Method: quicksort  Memory: xxx
+               Leader:  Sort Method: quicksort  Memory: xxx
                Worker 0:  Sort Method: quicksort  Memory: xxx
                Worker 1:  Sort Method: quicksort  Memory: xxx
                Worker 2:  Sort Method: quicksort  Memory: xxx
-- 
2.23.0

