1 /*-------------------------------------------------------------------------
4 * Routines to handle sorting of relations.
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/executor/nodeSort.c
13 *-------------------------------------------------------------------------
18 #include "access/parallel.h"
19 #include "executor/execdebug.h"
20 #include "executor/nodeSort.h"
21 #include "miscadmin.h"
22 #include "utils/tuplesort.h"
25 /* ----------------------------------------------------------------
28 * Sorts tuples from the outer subtree of the node using tuplesort,
29 * which saves the results in a temporary file or memory. After the
30 * initial call, returns a tuple from the file with each call.
36 * -- the outer child is prepared to return the first tuple.
37 * ----------------------------------------------------------------
39 static TupleTableSlot *
40 ExecSort(PlanState *pstate)
42 SortState *node = castNode(SortState, pstate);
45 Tuplesortstate *tuplesortstate;
48 CHECK_FOR_INTERRUPTS();
51 * get state info from node
53 SO1_printf("ExecSort: %s\n",
56 estate = node->ss.ps.state;
57 dir = estate->es_direction;
58 tuplesortstate = (Tuplesortstate *) node->tuplesortstate;
61 * If first time through, read all tuples from outer plan and pass them to
62 * tuplesort.c. Subsequent calls just fetch tuples from tuplesort.
67 Sort *plannode = (Sort *) node->ss.ps.plan;
71 SO1_printf("ExecSort: %s\n",
75 * Want to scan subplan in the forward direction while creating the
78 estate->es_direction = ForwardScanDirection;
81 * Initialize tuplesort module.
83 SO1_printf("ExecSort: %s\n",
84 "calling tuplesort_begin");
86 outerNode = outerPlanState(node);
87 tupDesc = ExecGetResultType(outerNode);
89 tuplesortstate = tuplesort_begin_heap(tupDesc,
92 plannode->sortOperators,
96 NULL, node->randomAccess);
98 tuplesort_set_bound(tuplesortstate, node->bound);
99 node->tuplesortstate = (void *) tuplesortstate;
102 * Scan the subplan and feed all the tuples to tuplesort.
107 slot = ExecProcNode(outerNode);
112 tuplesort_puttupleslot(tuplesortstate, slot);
118 tuplesort_performsort(tuplesortstate);
121 * restore to user specified direction
123 estate->es_direction = dir;
126 * finally set the sorted flag to true
128 node->sort_Done = true;
129 node->bounded_Done = node->bounded;
130 node->bound_Done = node->bound;
131 if (node->shared_info && node->am_worker)
133 TuplesortInstrumentation *si;
135 Assert(IsParallelWorker());
136 Assert(ParallelWorkerNumber <= node->shared_info->num_workers);
137 si = &node->shared_info->sinstrument[ParallelWorkerNumber];
138 tuplesort_get_stats(tuplesortstate, si);
140 SO1_printf("ExecSort: %s\n", "sorting done");
143 SO1_printf("ExecSort: %s\n",
144 "retrieving tuple from tuplesort");
147 * Get the first or next tuple from tuplesort. Returns NULL if no more
148 * tuples. Note that we only rely on slot tuple remaining valid until the
149 * next fetch from the tuplesort.
151 slot = node->ss.ps.ps_ResultTupleSlot;
152 (void) tuplesort_gettupleslot(tuplesortstate,
153 ScanDirectionIsForward(dir),
158 /* ----------------------------------------------------------------
161 * Creates the run-time state information for the sort node
162 * produced by the planner and initializes its outer subtree.
163 * ----------------------------------------------------------------
166 ExecInitSort(Sort *node, EState *estate, int eflags)
168 SortState *sortstate;
170 SO1_printf("ExecInitSort: %s\n",
171 "initializing sort node");
174 * create state structure
176 sortstate = makeNode(SortState);
177 sortstate->ss.ps.plan = (Plan *) node;
178 sortstate->ss.ps.state = estate;
179 sortstate->ss.ps.ExecProcNode = ExecSort;
182 * We must have random access to the sort output to do backward scan or
183 * mark/restore. We also prefer to materialize the sort output if we
184 * might be called on to rewind and replay it many times.
186 sortstate->randomAccess = (eflags & (EXEC_FLAG_REWIND |
188 EXEC_FLAG_MARK)) != 0;
190 sortstate->bounded = false;
191 sortstate->sort_Done = false;
192 sortstate->tuplesortstate = NULL;
195 * Miscellaneous initialization
197 * Sort nodes don't initialize their ExprContexts because they never call
198 * ExecQual or ExecProject.
202 * initialize child nodes
204 * We shield the child node from the need to support REWIND, BACKWARD, or
207 eflags &= ~(EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK);
209 outerPlanState(sortstate) = ExecInitNode(outerPlan(node), estate, eflags);
212 * Initialize scan slot and type.
214 ExecCreateScanSlotFromOuterPlan(estate, &sortstate->ss, &TTSOpsVirtual);
217 * Initialize return slot and type. No need to initialize projection info
218 * because this node doesn't do projections.
220 ExecInitResultTupleSlotTL(&sortstate->ss.ps, &TTSOpsMinimalTuple);
221 sortstate->ss.ps.ps_ProjInfo = NULL;
223 SO1_printf("ExecInitSort: %s\n",
224 "sort node initialized");
229 /* ----------------------------------------------------------------
231 * ----------------------------------------------------------------
234 ExecEndSort(SortState *node)
236 SO1_printf("ExecEndSort: %s\n",
237 "shutting down sort node");
240 * clean out the tuple table
242 ExecClearTuple(node->ss.ss_ScanTupleSlot);
243 /* must drop pointer to sort result tuple */
244 ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
247 * Release tuplesort resources
249 if (node->tuplesortstate != NULL)
250 tuplesort_end((Tuplesortstate *) node->tuplesortstate);
251 node->tuplesortstate = NULL;
254 * shut down the subplan
256 ExecEndNode(outerPlanState(node));
258 SO1_printf("ExecEndSort: %s\n",
259 "sort node shutdown");
262 /* ----------------------------------------------------------------
265 * Calls tuplesort to save the current position in the sorted file.
266 * ----------------------------------------------------------------
269 ExecSortMarkPos(SortState *node)
272 * if we haven't sorted yet, just return
274 if (!node->sort_Done)
277 tuplesort_markpos((Tuplesortstate *) node->tuplesortstate);
280 /* ----------------------------------------------------------------
283 * Calls tuplesort to restore the last saved sort file position.
284 * ----------------------------------------------------------------
287 ExecSortRestrPos(SortState *node)
290 * if we haven't sorted yet, just return.
292 if (!node->sort_Done)
296 * restore the scan to the previously marked position
298 tuplesort_restorepos((Tuplesortstate *) node->tuplesortstate);
302 ExecReScanSort(SortState *node)
304 PlanState *outerPlan = outerPlanState(node);
307 * If we haven't sorted yet, just return. If outerplan's chgParam is not
308 * NULL then it will be re-scanned by ExecProcNode, else no reason to
311 if (!node->sort_Done)
314 /* must drop pointer to sort result tuple */
315 ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
318 * If subnode is to be rescanned then we forget previous sort results; we
319 * have to re-read the subplan and re-sort. Also must re-sort if the
320 * bounded-sort parameters changed or we didn't select randomAccess.
322 * Otherwise we can just rewind and rescan the sorted output.
324 if (outerPlan->chgParam != NULL ||
325 node->bounded != node->bounded_Done ||
326 node->bound != node->bound_Done ||
329 node->sort_Done = false;
330 tuplesort_end((Tuplesortstate *) node->tuplesortstate);
331 node->tuplesortstate = NULL;
334 * if chgParam of subnode is not null then plan will be re-scanned by
335 * first ExecProcNode.
337 if (outerPlan->chgParam == NULL)
338 ExecReScan(outerPlan);
341 tuplesort_rescan((Tuplesortstate *) node->tuplesortstate);
344 /* ----------------------------------------------------------------
345 * Parallel Query Support
346 * ----------------------------------------------------------------
349 /* ----------------------------------------------------------------
352 * Estimate space required to propagate sort statistics.
353 * ----------------------------------------------------------------
356 ExecSortEstimate(SortState *node, ParallelContext *pcxt)
360 /* don't need this if not instrumenting or no workers */
361 if (!node->ss.ps.instrument || pcxt->nworkers == 0)
364 size = mul_size(pcxt->nworkers, sizeof(TuplesortInstrumentation));
365 size = add_size(size, offsetof(SharedSortInfo, sinstrument));
366 shm_toc_estimate_chunk(&pcxt->estimator, size);
367 shm_toc_estimate_keys(&pcxt->estimator, 1);
370 /* ----------------------------------------------------------------
371 * ExecSortInitializeDSM
373 * Initialize DSM space for sort statistics.
374 * ----------------------------------------------------------------
377 ExecSortInitializeDSM(SortState *node, ParallelContext *pcxt)
381 /* don't need this if not instrumenting or no workers */
382 if (!node->ss.ps.instrument || pcxt->nworkers == 0)
385 size = offsetof(SharedSortInfo, sinstrument)
386 + pcxt->nworkers * sizeof(TuplesortInstrumentation);
387 node->shared_info = shm_toc_allocate(pcxt->toc, size);
388 /* ensure any unfilled slots will contain zeroes */
389 memset(node->shared_info, 0, size);
390 node->shared_info->num_workers = pcxt->nworkers;
391 shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id,
395 /* ----------------------------------------------------------------
396 * ExecSortInitializeWorker
398 * Attach worker to DSM space for sort statistics.
399 * ----------------------------------------------------------------
402 ExecSortInitializeWorker(SortState *node, ParallelWorkerContext *pwcxt)
405 shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, true);
406 node->am_worker = true;
409 /* ----------------------------------------------------------------
410 * ExecSortRetrieveInstrumentation
412 * Transfer sort statistics from DSM to private memory.
413 * ----------------------------------------------------------------
416 ExecSortRetrieveInstrumentation(SortState *node)
421 if (node->shared_info == NULL)
424 size = offsetof(SharedSortInfo, sinstrument)
425 + node->shared_info->num_workers * sizeof(TuplesortInstrumentation);
427 memcpy(si, node->shared_info, size);
428 node->shared_info = si;