From 5a097cb4f7084b9f107662c56bf715a3309f12e5 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Fri, 25 Aug 2017 08:46:52 -0400
Subject: [PATCH 2/2] propagate sort instrumentation

---
 src/backend/commands/explain.c          |  57 +++++++++++-
 src/backend/executor/execParallel.c     | 155 ++++++++++++++++++--------------
 src/backend/executor/nodeSort.c         | 102 +++++++++++++++++++++
 src/backend/utils/sort/tuplesort.c      |  53 +++++++++--
 src/include/executor/nodeSort.h         |   7 ++
 src/include/nodes/execnodes.h           |  23 +++++
 src/include/utils/tuplesort.h           |  22 ++++-
 src/test/regress/expected/subselect.out |   3 +-
 src/test/regress/sql/subselect.sql      |   3 +-
 9 files changed, 343 insertions(+), 82 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 953e74d73c..55d275db5d 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -2292,15 +2292,21 @@ show_tablesample(TableSampleClause *tsc, PlanState *planstate,
 static void
 show_sort_info(SortState *sortstate, ExplainState *es)
 {
-	if (es->analyze && sortstate->sort_Done &&
-		sortstate->tuplesortstate != NULL)
+	if (!es->analyze)
+		return;
+
+	if (sortstate->sort_Done && sortstate->tuplesortstate != NULL)
 	{
 		Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate;
+		TuplesortMethod rawMethod;
+		TuplesortSpaceType rawSpaceType;
 		const char *sortMethod;
 		const char *spaceType;
 		long		spaceUsed;
 
-		tuplesort_get_stats(state, &sortMethod, &spaceType, &spaceUsed);
+		tuplesort_get_stats(state, &rawMethod, &rawSpaceType, &spaceUsed);
+		sortMethod = tuplesort_method_name(rawMethod);
+		spaceType = tuplesort_space_type_name(rawSpaceType);
 
 		if (es->format == EXPLAIN_FORMAT_TEXT)
 		{
@@ -2315,6 +2321,51 @@ show_sort_info(SortState *sortstate, ExplainState *es)
 			ExplainPropertyText("Sort Space Type", spaceType, es);
 		}
 	}
+
+	if (sortstate->shared_info != NULL)
+	{
+		int			n;
+		bool		opened_group = false;
+
+		for (n = 0; n < sortstate->shared_info->num_workers; ++n)
+		{
+			SortInstrumentation *sinstrument;
+			const char *sortMethod;
+			const char *spaceType;
+			long		spaceUsed;
+
+			sinstrument = &sortstate->shared_info->sinstrument[n];
+			if (sinstrument->sortMethod == SORT_TYPE_UNKNOWN)
+				continue;
+			sortMethod = tuplesort_method_name(sinstrument->sortMethod);
+			spaceType = tuplesort_space_type_name(sinstrument->spaceType);
+			spaceUsed = sinstrument->spaceUsed;
+
+			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", n, es);
+				ExplainPropertyText("Sort Method", sortMethod, es);
+				ExplainPropertyLong("Sort Space Used", spaceUsed, es);
+				ExplainPropertyText("Sort Space Type", spaceType, es);
+				ExplainCloseGroup("Worker", NULL, true, es);
+			}
+			if (opened_group)
+				ExplainCloseGroup("Workers", "Workers", false, es);
+		}
+	}
 }
 
 /*
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index 813a80e42d..d99759a4a8 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -28,9 +28,10 @@
 #include "executor/nodeBitmapHeapscan.h"
 #include "executor/nodeCustom.h"
 #include "executor/nodeForeignscan.h"
-#include "executor/nodeSeqscan.h"
 #include "executor/nodeIndexscan.h"
 #include "executor/nodeIndexonlyscan.h"
+#include "executor/nodeSeqscan.h"
+#include "executor/nodeSort.h"
 #include "executor/tqueue.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/planmain.h"
@@ -200,10 +201,10 @@ ExecSerializePlan(Plan *plan, EState *estate)
 }
 
 /*
- * Ordinary plan nodes won't do anything here, but parallel-aware plan nodes
- * may need some state which is shared across all parallel workers.  Before
- * we size the DSM, give them a chance to call shm_toc_estimate_chunk or
- * shm_toc_estimate_keys on &pcxt->estimator.
+ * Parallel-aware plan nodes (and occasionally others) may need some state
+ * which is shared across all parallel workers.  Before we size the DSM, give
+ * them a chance to call shm_toc_estimate_chunk or shm_toc_estimate_keys on
+ * &pcxt->estimator.
  *
  * While we're at it, count the number of PlanState nodes in the tree, so
  * we know how many SharedPlanStateInstrumentation structures we need.
@@ -217,38 +218,43 @@ ExecParallelEstimate(PlanState *planstate, ExecParallelEstimateContext *e)
 	/* Count this node. */
 	e->nnodes++;
 
-	/* Call estimators for parallel-aware nodes. */
-	if (planstate->plan->parallel_aware)
+	switch (nodeTag(planstate))
 	{
-		switch (nodeTag(planstate))
-		{
-			case T_SeqScanState:
+		case T_SeqScanState:
+			if (planstate->plan->parallel_aware)
 				ExecSeqScanEstimate((SeqScanState *) planstate,
 									e->pcxt);
-				break;
-			case T_IndexScanState:
+			break;
+		case T_IndexScanState:
+			if (planstate->plan->parallel_aware)
 				ExecIndexScanEstimate((IndexScanState *) planstate,
 									  e->pcxt);
-				break;
-			case T_IndexOnlyScanState:
+			break;
+		case T_IndexOnlyScanState:
+			if (planstate->plan->parallel_aware)
 				ExecIndexOnlyScanEstimate((IndexOnlyScanState *) planstate,
 										  e->pcxt);
-				break;
-			case T_ForeignScanState:
+			break;
+		case T_ForeignScanState:
+			if (planstate->plan->parallel_aware)
 				ExecForeignScanEstimate((ForeignScanState *) planstate,
 										e->pcxt);
-				break;
-			case T_CustomScanState:
+			break;
+		case T_CustomScanState:
+			if (planstate->plan->parallel_aware)
 				ExecCustomScanEstimate((CustomScanState *) planstate,
 									   e->pcxt);
-				break;
-			case T_BitmapHeapScanState:
+			break;
+		case T_BitmapHeapScanState:
+			if (planstate->plan->parallel_aware)
 				ExecBitmapHeapEstimate((BitmapHeapScanState *) planstate,
 									   e->pcxt);
-				break;
-			default:
-				break;
-		}
+			break;
+		case T_SortState:
+			/* even when not parallel-aware */
+			ExecSortEstimate((SortState *) planstate, e->pcxt);
+		default:
+			break;
 	}
 
 	return planstate_tree_walker(planstate, ExecParallelEstimate, e);
@@ -274,46 +280,51 @@ ExecParallelInitializeDSM(PlanState *planstate,
 	d->nnodes++;
 
 	/*
-	 * Call initializers for parallel-aware plan nodes.
+	 * Call initializers for DSM-using plan nodes.
 	 *
-	 * Ordinary plan nodes won't do anything here, but parallel-aware plan
-	 * nodes may need to initialize shared state in the DSM before parallel
-	 * workers are available.  They can allocate the space they previously
+	 * Most plan nodes won't do anything here, but plan nodes that allocated
+	 * DSM may need to initialize shared state in the DSM before parallel
+	 * workers are launched.  They can allocate the space they previously
 	 * estimated using shm_toc_allocate, and add the keys they previously
 	 * estimated using shm_toc_insert, in each case targeting pcxt->toc.
 	 */
-	if (planstate->plan->parallel_aware)
+	switch (nodeTag(planstate))
 	{
-		switch (nodeTag(planstate))
-		{
-			case T_SeqScanState:
+		case T_SeqScanState:
+			if (planstate->plan->parallel_aware)
 				ExecSeqScanInitializeDSM((SeqScanState *) planstate,
 										 d->pcxt);
-				break;
-			case T_IndexScanState:
+			break;
+		case T_IndexScanState:
+			if (planstate->plan->parallel_aware)
 				ExecIndexScanInitializeDSM((IndexScanState *) planstate,
 										   d->pcxt);
-				break;
-			case T_IndexOnlyScanState:
+			break;
+		case T_IndexOnlyScanState:
+			if (planstate->plan->parallel_aware)
 				ExecIndexOnlyScanInitializeDSM((IndexOnlyScanState *) planstate,
 											   d->pcxt);
-				break;
-			case T_ForeignScanState:
+			break;
+		case T_ForeignScanState:
+			if (planstate->plan->parallel_aware)
 				ExecForeignScanInitializeDSM((ForeignScanState *) planstate,
 											 d->pcxt);
-				break;
-			case T_CustomScanState:
+			break;
+		case T_CustomScanState:
+			if (planstate->plan->parallel_aware)
 				ExecCustomScanInitializeDSM((CustomScanState *) planstate,
 											d->pcxt);
-				break;
-			case T_BitmapHeapScanState:
+			break;
+		case T_BitmapHeapScanState:
+			if (planstate->plan->parallel_aware)
 				ExecBitmapHeapInitializeDSM((BitmapHeapScanState *) planstate,
 											d->pcxt);
-				break;
-
-			default:
-				break;
-		}
+			break;
+		case T_SortState:
+			/* even when not parallel-aware */
+			ExecSortInitializeDSM((SortState *) planstate, d->pcxt);
+		default:
+			break;
 	}
 
 	return planstate_tree_walker(planstate, ExecParallelInitializeDSM, d);
@@ -640,6 +651,13 @@ ExecParallelRetrieveInstrumentation(PlanState *planstate,
 	planstate->worker_instrument->num_workers = instrumentation->num_workers;
 	memcpy(&planstate->worker_instrument->instrument, instrument, ibytes);
 
+	/*
+	 * Perform any node-type-specific work that needs to be done.  Currently,
+	 * only Sort nodes need to do anything here.
+	 */
+	if (IsA(planstate, SortState))
+		ExecSortRetrieveInstrumentation((SortState *) planstate);
+
 	return planstate_tree_walker(planstate, ExecParallelRetrieveInstrumentation,
 								 instrumentation);
 }
@@ -799,35 +817,40 @@ ExecParallelInitializeWorker(PlanState *planstate, shm_toc *toc)
 	if (planstate == NULL)
 		return false;
 
-	/* Call initializers for parallel-aware plan nodes. */
-	if (planstate->plan->parallel_aware)
+	switch (nodeTag(planstate))
 	{
-		switch (nodeTag(planstate))
-		{
-			case T_SeqScanState:
+		case T_SeqScanState:
+			if (planstate->plan->parallel_aware)
 				ExecSeqScanInitializeWorker((SeqScanState *) planstate, toc);
-				break;
-			case T_IndexScanState:
+			break;
+		case T_IndexScanState:
+			if (planstate->plan->parallel_aware)
 				ExecIndexScanInitializeWorker((IndexScanState *) planstate, toc);
-				break;
-			case T_IndexOnlyScanState:
+			break;
+		case T_IndexOnlyScanState:
+			if (planstate->plan->parallel_aware)
 				ExecIndexOnlyScanInitializeWorker((IndexOnlyScanState *) planstate, toc);
-				break;
-			case T_ForeignScanState:
+			break;
+		case T_ForeignScanState:
+			if (planstate->plan->parallel_aware)
 				ExecForeignScanInitializeWorker((ForeignScanState *) planstate,
 												toc);
-				break;
-			case T_CustomScanState:
+			break;
+		case T_CustomScanState:
+			if (planstate->plan->parallel_aware)
 				ExecCustomScanInitializeWorker((CustomScanState *) planstate,
 											   toc);
-				break;
-			case T_BitmapHeapScanState:
+			break;
+		case T_BitmapHeapScanState:
+			if (planstate->plan->parallel_aware)
 				ExecBitmapHeapInitializeWorker(
 											   (BitmapHeapScanState *) planstate, toc);
-				break;
-			default:
-				break;
-		}
+			break;
+		case T_SortState:
+			/* even when not parallel-aware */
+			ExecSortInitializeWorker((SortState *) planstate, toc);
+		default:
+			break;
 	}
 
 	return planstate_tree_walker(planstate, ExecParallelInitializeWorker, toc);
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index aae4150e2c..abbc8ce12e 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -15,6 +15,7 @@
 
 #include "postgres.h"
 
+#include "access/parallel.h"
 #include "executor/execdebug.h"
 #include "executor/nodeSort.h"
 #include "miscadmin.h"
@@ -127,6 +128,18 @@ ExecSort(PlanState *pstate)
 		node->sort_Done = true;
 		node->bounded_Done = node->bounded;
 		node->bound_Done = node->bound;
+		if (node->shared_info && node->am_worker)
+		{
+			SortInstrumentation *si;
+
+			Assert(IsParallelWorker());
+			Assert(ParallelWorkerNumber <= node->shared_info->num_workers);
+			si = &node->shared_info->sinstrument[ParallelWorkerNumber];
+			tuplesort_get_stats(tuplesortstate,
+								&si->sortMethod,
+								&si->spaceType,
+								&si->spaceUsed);
+		}
 		SO1_printf("ExecSort: %s\n", "sorting done");
 	}
 
@@ -334,3 +347,92 @@ ExecReScanSort(SortState *node)
 	else
 		tuplesort_rescan((Tuplesortstate *) node->tuplesortstate);
 }
+
+/* ----------------------------------------------------------------
+ *						Parallel Query Support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ *		ExecSortEstimate
+ *
+ *		Estimate space required to propagate sort statistics.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSortEstimate(SortState *node, ParallelContext *pcxt)
+{
+	Size	size;
+
+	/* don't need this if not instrumenting or no workers */
+	if (!node->ss.ps.instrument || pcxt->nworkers == 0)
+		return;
+
+	size = mul_size(pcxt->nworkers, sizeof(SortInstrumentation));
+	size = add_size(size, offsetof(SharedSortInfo, sinstrument));
+	shm_toc_estimate_chunk(&pcxt->estimator, size);
+	shm_toc_estimate_keys(&pcxt->estimator, 1);
+}
+
+/* ----------------------------------------------------------------
+ *		ExecSortInitializeDSM
+ *
+ *		Initialize DSM space for sort statistics.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSortInitializeDSM(SortState *node, ParallelContext *pcxt)
+{
+	Size	size;
+
+	/* don't need this if not instrumenting or no workers */
+	if (!node->ss.ps.instrument || pcxt->nworkers == 0)
+		return;
+	if (pcxt->nworkers == 0)
+		return;
+
+	size = offsetof(SharedSortInfo, sinstrument)
+		+ pcxt->nworkers * sizeof(SortInstrumentation);
+	node->shared_info =
+		shm_toc_allocate(pcxt->toc,
+						 pcxt->nworkers * sizeof(SortInstrumentation));
+	node->shared_info->num_workers = pcxt->nworkers;
+	shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id,
+				   node->shared_info);
+}
+
+/* ----------------------------------------------------------------
+ *		ExecSortInitializeWorker
+ *
+ *		Attach worker to DSM space for sort statistics.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSortInitializeWorker(SortState *node, shm_toc *toc)
+{
+	node->shared_info =
+		shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id, true);
+	node->am_worker = true;
+}
+
+/* ----------------------------------------------------------------
+ *		ExecSortRetrieveInstrumentation
+ *
+ *		Transfer sort statitics from DSM to private memory.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSortRetrieveInstrumentation(SortState *node)
+{
+	Size size;
+	SharedSortInfo *si;
+
+	if (node->shared_info == NULL)
+		return;
+
+	size = offsetof(SharedSortInfo, sinstrument)
+		+ node->shared_info->num_workers * sizeof(SortInstrumentation);
+	si = palloc(size);
+	memcpy(si, node->shared_info, size);
+	node->shared_info = si;
+}
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 59cd28e595..3b61fecd22 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -3231,8 +3231,8 @@ tuplesort_restorepos(Tuplesortstate *state)
  */
 void
 tuplesort_get_stats(Tuplesortstate *state,
-					const char **sortMethod,
-					const char **spaceType,
+					TuplesortMethod *sortMethod,
+					TuplesortSpaceType *spaceType,
 					long *spaceUsed)
 {
 	/*
@@ -3246,12 +3246,12 @@ tuplesort_get_stats(Tuplesortstate *state,
 	 */
 	if (state->tapeset)
 	{
-		*spaceType = "Disk";
+		*spaceType = SORT_SPACE_TYPE_DISK;
 		*spaceUsed = LogicalTapeSetBlocks(state->tapeset) * (BLCKSZ / 1024);
 	}
 	else
 	{
-		*spaceType = "Memory";
+		*spaceType = SORT_SPACE_TYPE_MEMORY;
 		*spaceUsed = (state->allowedMem - state->availMem + 1023) / 1024;
 	}
 
@@ -3259,22 +3259,57 @@ tuplesort_get_stats(Tuplesortstate *state,
 	{
 		case TSS_SORTEDINMEM:
 			if (state->boundUsed)
-				*sortMethod = "top-N heapsort";
+				*sortMethod = SORT_TYPE_TOP_N_HEAPSORT;
 			else
-				*sortMethod = "quicksort";
+				*sortMethod = SORT_TYPE_QUICKSORT;
 			break;
 		case TSS_SORTEDONTAPE:
-			*sortMethod = "external sort";
+			*sortMethod = SORT_TYPE_EXTERNAL_SORT;
 			break;
 		case TSS_FINALMERGE:
-			*sortMethod = "external merge";
+			*sortMethod = SORT_TYPE_EXTERNAL_MERGE;
 			break;
 		default:
-			*sortMethod = "still in progress";
+			*sortMethod = SORT_TYPE_STILL_IN_PROGRESS;
 			break;
 	}
 }
 
+/*
+ * Convert TuplesortSpaceType to a string.
+ */
+const char *
+tuplesort_space_type_name(TuplesortSpaceType t)
+{
+	Assert(t == SORT_SPACE_TYPE_DISK || t == SORT_SPACE_TYPE_MEMORY);
+	return t == SORT_SPACE_TYPE_DISK ? "Disk" : "Memory";
+}
+
+/*
+ * Convert TuplesortMethod to a string.
+ */
+const char *
+tuplesort_method_name(TuplesortMethod m)
+{
+	switch (m)
+	{
+		case SORT_TYPE_UNKNOWN:
+			break;
+		case SORT_TYPE_TOP_N_HEAPSORT:
+			return "top-N heapsort";
+		case SORT_TYPE_QUICKSORT:
+			return "quicksort";
+		case SORT_TYPE_EXTERNAL_SORT:
+			return "external sort";
+		case SORT_TYPE_EXTERNAL_MERGE:
+			return "external merge";
+		case SORT_TYPE_STILL_IN_PROGRESS:
+			return "still in progress";
+	}
+
+	Assert(m == SORT_TYPE_UNKNOWN);
+	return "unknown";
+}
 
 /*
  * Heap manipulation routines, per Knuth's Algorithm 5.2.3H.
diff --git a/src/include/executor/nodeSort.h b/src/include/executor/nodeSort.h
index ed0e9dbb53..77ac06597f 100644
--- a/src/include/executor/nodeSort.h
+++ b/src/include/executor/nodeSort.h
@@ -14,6 +14,7 @@
 #ifndef NODESORT_H
 #define NODESORT_H
 
+#include "access/parallel.h"
 #include "nodes/execnodes.h"
 
 extern SortState *ExecInitSort(Sort *node, EState *estate, int eflags);
@@ -22,4 +23,10 @@ extern void ExecSortMarkPos(SortState *node);
 extern void ExecSortRestrPos(SortState *node);
 extern void ExecReScanSort(SortState *node);
 
+/* parallel instrumentation support */
+extern void ExecSortEstimate(SortState *node, ParallelContext *pcxt);
+extern void ExecSortInitializeDSM(SortState *node, ParallelContext *pcxt);
+extern void ExecSortInitializeWorker(SortState *node, shm_toc *toc);
+extern void ExecSortRetrieveInstrumentation(SortState *node);
+
 #endif							/* NODESORT_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 17ae657022..99cb0e37d0 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1731,6 +1731,27 @@ typedef struct MaterialState
 } MaterialState;
 
 /* ----------------
+ *	 Per-worker sort information
+ * ----------------
+ */
+typedef struct SortInstrumentation
+{
+	TuplesortMethod sortMethod;
+	TuplesortSpaceType spaceType;
+	long		spaceUsed;
+} SortInstrumentation;
+
+/* ----------------
+ *	 Shared memory container for per-worker sort information
+ * ----------------
+ */
+typedef struct SharedSortInfo
+{
+	int			num_workers;
+	SortInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
+} SharedSortInfo;
+
+/* ----------------
  *	 SortState information
  * ----------------
  */
@@ -1744,6 +1765,8 @@ typedef struct SortState
 	bool		bounded_Done;	/* value of bounded we did the sort with */
 	int64		bound_Done;		/* value of bound we did the sort with */
 	void	   *tuplesortstate; /* private state of tuplesort.c */
+	bool		am_worker;		/* are we a worker? */
+	SharedSortInfo *shared_info;	/* one entry per worker */
 } SortState;
 
 /* ---------------------
diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h
index 28c168a801..2c966af199 100644
--- a/src/include/utils/tuplesort.h
+++ b/src/include/utils/tuplesort.h
@@ -31,6 +31,22 @@
  */
 typedef struct Tuplesortstate Tuplesortstate;
 
+typedef enum
+{
+	SORT_SPACE_TYPE_DISK,
+	SORT_SPACE_TYPE_MEMORY
+} TuplesortSpaceType;
+
+typedef enum
+{
+	SORT_TYPE_UNKNOWN,
+	SORT_TYPE_TOP_N_HEAPSORT,
+	SORT_TYPE_QUICKSORT,
+	SORT_TYPE_EXTERNAL_SORT,
+	SORT_TYPE_EXTERNAL_MERGE,
+	SORT_TYPE_STILL_IN_PROGRESS,
+} TuplesortMethod;
+
 /*
  * We provide multiple interfaces to what is essentially the same code,
  * since different callers have different data to be sorted and want to
@@ -107,9 +123,11 @@ extern bool tuplesort_skiptuples(Tuplesortstate *state, int64 ntuples,
 extern void tuplesort_end(Tuplesortstate *state);
 
 extern void tuplesort_get_stats(Tuplesortstate *state,
-					const char **sortMethod,
-					const char **spaceType,
+					TuplesortMethod *sortMethod,
+					TuplesortSpaceType *spaceType,
 					long *spaceUsed);
+extern const char *tuplesort_method_name(TuplesortMethod);
+extern const char *tuplesort_space_type_name(TuplesortSpaceType);
 
 extern int	tuplesort_merge_order(int64 allowedMem);
 
diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out
index f009c253f4..d8dfdf33c8 100644
--- a/src/test/regress/expected/subselect.out
+++ b/src/test/regress/expected/subselect.out
@@ -1047,7 +1047,7 @@ drop function tattle(x int, y int);
 -- ANALYZE shows that a top-N sort was used.  We must suppress or filter away
 -- all the non-invariant parts of the EXPLAIN ANALYZE output.
 --
-create temp table sq_limit (pk int primary key, c1 int, c2 int);
+create table sq_limit (pk int primary key, c1 int, c2 int);
 insert into sq_limit values
     (1, 1, 1),
     (2, 2, 2),
@@ -1066,6 +1066,7 @@ begin
         select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3
     loop
         ln := regexp_replace(ln, 'Memory: \S*',  'Memory: xxx');
+        ln := regexp_replace(ln, 'Worker 0 Sort Method',  'Sort Method');
         return next ln;
     end loop;
 end;
diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql
index 9a14832206..4062eb81f4 100644
--- a/src/test/regress/sql/subselect.sql
+++ b/src/test/regress/sql/subselect.sql
@@ -547,7 +547,7 @@ drop function tattle(x int, y int);
 -- ANALYZE shows that a top-N sort was used.  We must suppress or filter away
 -- all the non-invariant parts of the EXPLAIN ANALYZE output.
 --
-create temp table sq_limit (pk int primary key, c1 int, c2 int);
+create table sq_limit (pk int primary key, c1 int, c2 int);
 insert into sq_limit values
     (1, 1, 1),
     (2, 2, 2),
@@ -567,6 +567,7 @@ begin
         select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3
     loop
         ln := regexp_replace(ln, 'Memory: \S*',  'Memory: xxx');
+        ln := regexp_replace(ln, 'Worker 0 Sort Method',  'Sort Method');
         return next ln;
     end loop;
 end;
-- 
2.11.0 (Apple Git-81)

