From 54f81da3169d488afee133053897a52f65481809 Mon Sep 17 00:00:00 2001
From: Arseny Sher <sher-ars@ispras.ru>
Date: Tue, 14 Mar 2017 20:03:41 +0300
Subject: [PATCH 8/8] Reversed in-memory Sort implementation.

Only in-memory sort is supported for now.
---
 src/backend/executor/execProcnode.c |  12 +++
 src/backend/executor/nodeSort.c     | 143 ++++++++++++------------------------
 src/backend/utils/sort/tuplesort.c  |  35 +++++++++
 src/include/executor/nodeSort.h     |   5 +-
 src/include/utils/tuplesort.h       |   3 +
 5 files changed, 98 insertions(+), 100 deletions(-)

diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 1aca5f0d75..a7e29a126b 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -168,6 +168,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags, PlanState *parent)
 		/*
 		 * materialization nodes
 		 */
+		case T_Sort:
+			result = (PlanState *) ExecInitSort((Sort *) node,
+												estate, eflags, parent);
+			break;
+
 		case T_Agg:
 			result = (PlanState *) ExecInitAgg((Agg *) node,
 											   estate, eflags, parent);
@@ -261,6 +266,9 @@ pushTuple(TupleTableSlot *slot, PlanState *node, PlanState *pusher)
 	if (nodeTag(node) == T_LimitState)
 		return pushTupleToLimit(slot, (LimitState *) node);
 
+	else if (nodeTag(node) == T_SortState)
+		return pushTupleToSort(slot, (SortState *) node);
+
 	else if (nodeTag(node) == T_AggState)
 		return pushTupleToAgg(slot, (AggState *) node);
 
@@ -332,6 +340,10 @@ ExecEndNode(PlanState *node)
 		/*
 		 * materialization nodes
 		 */
+		case T_SortState:
+			ExecEndSort((SortState *) node);
+			break;
+
 		case T_AggState:
 			ExecEndAgg((AggState *) node);
 			break;
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index 0028912509..8ba501e9ac 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -22,11 +22,11 @@
 
 
 /* ----------------------------------------------------------------
- *		ExecSort
+ *		pushTupleToSort
  *
  *		Sorts tuples from the outer subtree of the node using tuplesort,
- *		which saves the results in a temporary file or memory. After the
- *		initial call, returns a tuple from the file with each call.
+ *		which saves the results in a temporary file or memory. After receiving
+ *		NULL slot, pushes all tuples.
  *
  *		Conditions:
  *		  -- none.
@@ -35,110 +35,55 @@
  *		  -- the outer child is prepared to return the first tuple.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecSort(SortState *node)
+bool
+pushTupleToSort(TupleTableSlot *slot, SortState *node)
 {
-	EState	   *estate;
-	ScanDirection dir;
-	Tuplesortstate *tuplesortstate;
-	TupleTableSlot *slot;
+	Sort	   *plannode = (Sort *) node->ss.ps.plan;
+	PlanState  *outerNode;
+	TupleDesc	tupDesc;
 
-	/*
-	 * get state info from node
-	 */
-	SO1_printf("ExecSort: %s\n",
-			   "entering routine");
-
-	estate = node->ss.ps.state;
-	dir = estate->es_direction;
-	tuplesortstate = (Tuplesortstate *) node->tuplesortstate;
-
-	/*
-	 * If first time through, read all tuples from outer plan and pass them to
-	 * tuplesort.c. Subsequent calls just fetch tuples from tuplesort.
-	 */
+	/* bounded nodes not supported yet */
+	Assert(!node->bounded);
+	/* only forward direction is supported for now */
+	Assert(ScanDirectionIsForward(node->ss.ps.state->es_direction));
+	Assert(!node->sort_Done);
 
-	if (!node->sort_Done)
+	if (!TupIsNull(slot))
 	{
-		Sort	   *plannode = (Sort *) node->ss.ps.plan;
-		PlanState  *outerNode;
-		TupleDesc	tupDesc;
-
-		SO1_printf("ExecSort: %s\n",
-				   "sorting subplan");
-
-		/*
-		 * Want to scan subplan in the forward direction while creating the
-		 * sorted data.
-		 */
-		estate->es_direction = ForwardScanDirection;
-
-		/*
-		 * Initialize tuplesort module.
-		 */
-		SO1_printf("ExecSort: %s\n",
-				   "calling tuplesort_begin");
-
-		outerNode = outerPlanState(node);
-		tupDesc = ExecGetResultType(outerNode);
-
-		tuplesortstate = tuplesort_begin_heap(tupDesc,
-											  plannode->numCols,
-											  plannode->sortColIdx,
-											  plannode->sortOperators,
-											  plannode->collations,
-											  plannode->nullsFirst,
-											  work_mem,
-											  node->randomAccess);
-		if (node->bounded)
-			tuplesort_set_bound(tuplesortstate, node->bound);
-		node->tuplesortstate = (void *) tuplesortstate;
-
-		/*
-		 * Scan the subplan and feed all the tuples to tuplesort.
-		 */
-
-		for (;;)
+		if (node->tuplesortstate == NULL)
 		{
-			slot = ExecProcNode(outerNode);
-
-			if (TupIsNull(slot))
-				break;
-
-			tuplesort_puttupleslot(tuplesortstate, slot);
+			/* first call, time to create tuplesort */
+			outerNode = outerPlanState(node);
+			tupDesc = ExecGetResultType(outerNode);
+
+			node->tuplesortstate = tuplesort_begin_heap(tupDesc,
+														plannode->numCols,
+														plannode->sortColIdx,
+														plannode->sortOperators,
+														plannode->collations,
+														plannode->nullsFirst,
+														work_mem,
+														node->randomAccess);
 		}
-
-		/*
-		 * Complete the sort.
-		 */
-		tuplesort_performsort(tuplesortstate);
-
-		/*
-		 * restore to user specified direction
-		 */
-		estate->es_direction = dir;
-
-		/*
-		 * finally set the sorted flag to true
-		 */
-		node->sort_Done = true;
-		node->bounded_Done = node->bounded;
-		node->bound_Done = node->bound;
-		SO1_printf("ExecSort: %s\n", "sorting done");
+		/* feed the tuple to tuplesort */
+		tuplesort_puttupleslot(node->tuplesortstate, slot);
+		return true;
 	}
 
-	SO1_printf("ExecSort: %s\n",
-			   "retrieving tuple from tuplesort");
+	/* NULL tuple arrived, sort and push tuples */
 
 	/*
-	 * Get the first or next tuple from tuplesort. Returns NULL if no more
-	 * tuples.
+	 * Complete the sort.
 	 */
-	slot = node->ss.ps.ps_ResultTupleSlot;
-	(void) tuplesort_gettupleslot(tuplesortstate,
-								  ScanDirectionIsForward(dir),
-								  slot, NULL);
-	return slot;
+	tuplesort_performsort(node->tuplesortstate);
+	node->sort_Done = true;
+
+	if (tuplesort_pushtuples(node->tuplesortstate, node))
+		/* If parent still waits for tuples, let it know we are done */
+		pushTuple(NULL, node->ss.ps.parent, (PlanState *) node);
+
+	/* doesn't matter */
+	return false;
 }
 
 /* ----------------------------------------------------------------
@@ -149,7 +94,7 @@ ExecSort(SortState *node)
  * ----------------------------------------------------------------
  */
 SortState *
-ExecInitSort(Sort *node, EState *estate, int eflags)
+ExecInitSort(Sort *node, EState *estate, int eflags, PlanState *parent)
 {
 	SortState  *sortstate;
 
@@ -162,6 +107,7 @@ ExecInitSort(Sort *node, EState *estate, int eflags)
 	sortstate = makeNode(SortState);
 	sortstate->ss.ps.plan = (Plan *) node;
 	sortstate->ss.ps.state = estate;
+	sortstate->ss.ps.parent = parent;
 
 	/*
 	 * We must have random access to the sort output to do backward scan or
@@ -199,7 +145,8 @@ ExecInitSort(Sort *node, EState *estate, int eflags)
 	 */
 	eflags &= ~(EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK);
 
-	outerPlanState(sortstate) = ExecInitNode(outerPlan(node), estate, eflags, NULL);
+	outerPlanState(sortstate) = ExecInitNode(outerPlan(node), estate, eflags,
+											 (PlanState *) sortstate);
 
 	/*
 	 * initialize tuple type.  no need to initialize projection info because
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index e1e692d5f0..96ffadafc5 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -2080,6 +2080,41 @@ tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
 }
 
 /*
+ * Push every tuple from tuplesort. Returns last pushTuple() result.
+ */
+bool
+tuplesort_pushtuples(Tuplesortstate *state, SortState *node)
+{
+	SortTuple	stup;
+	MemoryContext oldcontext = CurrentMemoryContext;
+	TupleTableSlot *slot = node->ss.ps.ps_ResultTupleSlot;
+	bool parent_accepts_tuples = true;
+
+	/* only in mem sort is supported for now */
+	Assert(state->status == TSS_SORTEDINMEM);
+	Assert(!state->slabAllocatorUsed);
+
+	while (state->current < state->memtupcount)
+	{
+		/* Imitating context switching as it was before */
+		MemoryContextSwitchTo(state->sortcontext);
+		stup = state->memtuples[state->current++];
+		MemoryContextSwitchTo(oldcontext);
+
+		stup.tuple = heap_copy_minimal_tuple((MinimalTuple) stup.tuple);
+		ExecStoreMinimalTuple((MinimalTuple) stup.tuple, slot, true);
+		parent_accepts_tuples =
+			pushTuple(slot, node->ss.ps.parent, (PlanState *) node);
+		if (!parent_accepts_tuples)
+			return false;
+	}
+
+	state->eof_reached = true;
+	ExecClearTuple(slot);
+	return parent_accepts_tuples;
+}
+
+/*
  * Fetch the next tuple in either forward or back direction.
  * If successful, put tuple in slot and return TRUE; else, clear the slot
  * and return FALSE.
diff --git a/src/include/executor/nodeSort.h b/src/include/executor/nodeSort.h
index 10d16b47b1..27a7fb02d5 100644
--- a/src/include/executor/nodeSort.h
+++ b/src/include/executor/nodeSort.h
@@ -16,8 +16,9 @@
 
 #include "nodes/execnodes.h"
 
-extern SortState *ExecInitSort(Sort *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecSort(SortState *node);
+extern SortState *ExecInitSort(Sort *node, EState *estate, int eflags,
+							   PlanState *parent);
+extern bool pushTupleToSort(TupleTableSlot *slot, SortState *node);
 extern void ExecEndSort(SortState *node);
 extern void ExecSortMarkPos(SortState *node);
 extern void ExecSortRestrPos(SortState *node);
diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h
index 5b3f4752f4..a3770c3af8 100644
--- a/src/include/utils/tuplesort.h
+++ b/src/include/utils/tuplesort.h
@@ -92,6 +92,9 @@ extern void tuplesort_putdatum(Tuplesortstate *state, Datum val,
 
 extern void tuplesort_performsort(Tuplesortstate *state);
 
+/* forward decl, since now we need to know about SortState */
+typedef struct SortState SortState;
+extern bool tuplesort_pushtuples(Tuplesortstate *state, SortState *node);
 extern bool tuplesort_gettupleslot(Tuplesortstate *state, bool forward,
 					   TupleTableSlot *slot, Datum *abbrev);
 extern HeapTuple tuplesort_getheaptuple(Tuplesortstate *state, bool forward);
-- 
2.11.0

