Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * nodeSort.c
4 : : * Routines to handle sorting of relations.
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/executor/nodeSort.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : :
16 : : #include "postgres.h"
17 : :
18 : : #include "access/parallel.h"
19 : : #include "executor/executor.h"
20 : : #include "executor/nodeSort.h"
21 : : #include "miscadmin.h"
22 : : #include "utils/tuplesort.h"
23 : :
24 : :
25 : : /* ----------------------------------------------------------------
26 : : * ExecSort
27 : : *
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.
31 : : *
32 : : * There are two distinct ways that this sort can be performed:
33 : : *
34 : : * 1) When the result is a single column we perform a Datum sort.
35 : : *
36 : : * 2) When the result contains multiple columns we perform a tuple sort.
37 : : *
38 : : * We could do this by always performing a tuple sort, however sorting
39 : : * Datums only can be significantly faster than sorting tuples,
40 : : * especially when the Datums are of a pass-by-value type.
41 : : *
42 : : * Conditions:
43 : : * -- none.
44 : : *
45 : : * Initial States:
46 : : * -- the outer child is prepared to return the first tuple.
47 : : * ----------------------------------------------------------------
48 : : */
49 : : static TupleTableSlot *
50 : 7314051 : ExecSort(PlanState *pstate)
51 : : {
52 : 7314051 : SortState *node = castNode(SortState, pstate);
53 : : EState *estate;
54 : : ScanDirection dir;
55 : : Tuplesortstate *tuplesortstate;
56 : : TupleTableSlot *slot;
57 : :
58 [ + + ]: 7314051 : CHECK_FOR_INTERRUPTS();
59 : :
60 : : /*
61 : : * get state info from node
62 : : */
63 : 7314051 : estate = node->ss.ps.state;
64 : 7314051 : dir = estate->es_direction;
65 : 7314051 : tuplesortstate = (Tuplesortstate *) node->tuplesortstate;
66 : :
67 : : /*
68 : : * If first time through, read all tuples from outer plan and pass them to
69 : : * tuplesort.c. Subsequent calls just fetch tuples from tuplesort.
70 : : */
71 : :
72 [ + + ]: 7314051 : if (!node->sort_Done)
73 : : {
74 : 77653 : Sort *plannode = (Sort *) node->ss.ps.plan;
75 : : PlanState *outerNode;
76 : : TupleDesc tupDesc;
77 : 77653 : int tuplesortopts = TUPLESORT_NONE;
78 : :
79 : : /*
80 : : * Want to scan subplan in the forward direction while creating the
81 : : * sorted data.
82 : : */
83 : 77653 : estate->es_direction = ForwardScanDirection;
84 : :
85 : : /*
86 : : * Initialize tuplesort module.
87 : : */
88 : 77653 : outerNode = outerPlanState(node);
89 : 77653 : tupDesc = ExecGetResultType(outerNode);
90 : :
91 [ + + ]: 77653 : if (node->randomAccess)
92 : 3712 : tuplesortopts |= TUPLESORT_RANDOMACCESS;
93 [ + + ]: 77653 : if (node->bounded)
94 : 627 : tuplesortopts |= TUPLESORT_ALLOWBOUNDED;
95 : :
96 [ + + ]: 77653 : if (node->datumSort)
97 : 5671 : tuplesortstate = tuplesort_begin_datum(TupleDescAttr(tupDesc, 0)->atttypid,
98 : 5671 : plannode->sortOperators[0],
99 : 5671 : plannode->collations[0],
100 : 5671 : plannode->nullsFirst[0],
101 : : work_mem,
102 : : NULL,
103 : : tuplesortopts);
104 : : else
105 : 71982 : tuplesortstate = tuplesort_begin_heap(tupDesc,
106 : : plannode->numCols,
107 : : plannode->sortColIdx,
108 : : plannode->sortOperators,
109 : : plannode->collations,
110 : : plannode->nullsFirst,
111 : : work_mem,
112 : : NULL,
113 : : tuplesortopts);
114 [ + + ]: 77645 : if (node->bounded)
115 : 627 : tuplesort_set_bound(tuplesortstate, node->bound);
116 : 77645 : node->tuplesortstate = tuplesortstate;
117 : :
118 : : /*
119 : : * Scan the subplan and feed all the tuples to tuplesort using the
120 : : * appropriate method based on the type of sort we're doing.
121 : : */
122 [ + + ]: 77645 : if (node->datumSort)
123 : : {
124 : : for (;;)
125 : : {
126 : 1030528 : slot = ExecProcNode(outerNode);
127 : :
128 [ + + + + ]: 1030518 : if (TupIsNull(slot))
129 : : break;
130 : 1024857 : slot_getsomeattrs(slot, 1);
131 : 1024857 : tuplesort_putdatum(tuplesortstate,
132 : 1024857 : slot->tts_values[0],
133 : 1024857 : slot->tts_isnull[0]);
134 : : }
135 : : }
136 : : else
137 : : {
138 : : for (;;)
139 : : {
140 : 7181989 : slot = ExecProcNode(outerNode);
141 : :
142 [ + + + + ]: 7181988 : if (TupIsNull(slot))
143 : : break;
144 : 7110015 : tuplesort_puttupleslot(tuplesortstate, slot);
145 : : }
146 : : }
147 : :
148 : : /*
149 : : * Complete the sort.
150 : : */
151 : 77634 : tuplesort_performsort(tuplesortstate);
152 : :
153 : : /*
154 : : * restore to user specified direction
155 : : */
156 : 77634 : estate->es_direction = dir;
157 : :
158 : : /*
159 : : * finally set the sorted flag to true
160 : : */
161 : 77634 : node->sort_Done = true;
162 : 77634 : node->bounded_Done = node->bounded;
163 : 77634 : node->bound_Done = node->bound;
164 [ + + + + ]: 77634 : if (node->shared_info && node->am_worker)
165 : : {
166 : : TuplesortInstrumentation *si;
167 : :
168 : : Assert(IsParallelWorker());
169 : : Assert(ParallelWorkerNumber < node->shared_info->num_workers);
170 : 64 : si = &node->shared_info->sinstrument[ParallelWorkerNumber];
171 : 64 : tuplesort_get_stats(tuplesortstate, si);
172 : : }
173 : : }
174 : :
175 : 7314032 : slot = node->ss.ps.ps_ResultTupleSlot;
176 : :
177 : : /*
178 : : * Fetch the next sorted item from the appropriate tuplesort function. For
179 : : * datum sorts we must manage the slot ourselves and leave it clear when
180 : : * tuplesort_getdatum returns false to indicate there are no more datums.
181 : : * For tuple sorts, tuplesort_gettupleslot manages the slot for us and
182 : : * empties the slot when it runs out of tuples.
183 : : */
184 [ + + ]: 7314032 : if (node->datumSort)
185 : : {
186 : 832408 : ExecClearTuple(slot);
187 [ + + ]: 832408 : if (tuplesort_getdatum(tuplesortstate, ScanDirectionIsForward(dir),
188 : : false, &(slot->tts_values[0]),
189 : : &(slot->tts_isnull[0]), NULL))
190 : 826956 : ExecStoreVirtualTuple(slot);
191 : : }
192 : : else
193 : 6481624 : (void) tuplesort_gettupleslot(tuplesortstate,
194 : : ScanDirectionIsForward(dir),
195 : : false, slot, NULL);
196 : :
197 : 7314032 : return slot;
198 : : }
199 : :
200 : : /* ----------------------------------------------------------------
201 : : * ExecInitSort
202 : : *
203 : : * Creates the run-time state information for the sort node
204 : : * produced by the planner and initializes its outer subtree.
205 : : * ----------------------------------------------------------------
206 : : */
207 : : SortState *
208 : 55787 : ExecInitSort(Sort *node, EState *estate, int eflags)
209 : : {
210 : : SortState *sortstate;
211 : : TupleDesc outerTupDesc;
212 : :
213 : : /*
214 : : * create state structure
215 : : */
216 : 55787 : sortstate = makeNode(SortState);
217 : 55787 : sortstate->ss.ps.plan = (Plan *) node;
218 : 55787 : sortstate->ss.ps.state = estate;
219 : 55787 : sortstate->ss.ps.ExecProcNode = ExecSort;
220 : :
221 : : /*
222 : : * We must have random access to the sort output to do backward scan or
223 : : * mark/restore. We also prefer to materialize the sort output if we
224 : : * might be called on to rewind and replay it many times.
225 : : */
226 : 55787 : sortstate->randomAccess = (eflags & (EXEC_FLAG_REWIND |
227 : : EXEC_FLAG_BACKWARD |
228 : 55787 : EXEC_FLAG_MARK)) != 0;
229 : :
230 : 55787 : sortstate->bounded = false;
231 : 55787 : sortstate->sort_Done = false;
232 : 55787 : sortstate->tuplesortstate = NULL;
233 : :
234 : : /*
235 : : * Miscellaneous initialization
236 : : *
237 : : * Sort nodes don't initialize their ExprContexts because they never call
238 : : * ExecQual or ExecProject.
239 : : */
240 : :
241 : : /*
242 : : * initialize child nodes
243 : : *
244 : : * We shield the child node from the need to support REWIND, BACKWARD, or
245 : : * MARK/RESTORE.
246 : : */
247 : 55787 : eflags &= ~(EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK);
248 : :
249 : 55787 : outerPlanState(sortstate) = ExecInitNode(outerPlan(node), estate, eflags);
250 : :
251 : : /*
252 : : * Initialize scan slot and type.
253 : : */
254 : 55783 : ExecCreateScanSlotFromOuterPlan(estate, &sortstate->ss, &TTSOpsVirtual);
255 : :
256 : : /*
257 : : * Initialize return slot and type. No need to initialize projection info
258 : : * because this node doesn't do projections.
259 : : */
260 : 55783 : ExecInitResultTupleSlotTL(&sortstate->ss.ps, &TTSOpsMinimalTuple);
261 : 55783 : sortstate->ss.ps.ps_ProjInfo = NULL;
262 : :
263 : 55783 : outerTupDesc = ExecGetResultType(outerPlanState(sortstate));
264 : :
265 : : /*
266 : : * We perform a Datum sort when we're sorting just a single column,
267 : : * otherwise we perform a tuple sort.
268 : : */
269 [ + + ]: 55783 : if (outerTupDesc->natts == 1)
270 : 8303 : sortstate->datumSort = true;
271 : : else
272 : 47480 : sortstate->datumSort = false;
273 : :
274 : 55783 : return sortstate;
275 : : }
276 : :
277 : : /* ----------------------------------------------------------------
278 : : * ExecEndSort(node)
279 : : * ----------------------------------------------------------------
280 : : */
281 : : void
282 : 55676 : ExecEndSort(SortState *node)
283 : : {
284 : : /*
285 : : * Release tuplesort resources
286 : : */
287 [ + + ]: 55676 : if (node->tuplesortstate != NULL)
288 : 49317 : tuplesort_end((Tuplesortstate *) node->tuplesortstate);
289 : 55676 : node->tuplesortstate = NULL;
290 : :
291 : : /*
292 : : * shut down the subplan
293 : : */
294 : 55676 : ExecEndNode(outerPlanState(node));
295 : 55676 : }
296 : :
297 : : /* ----------------------------------------------------------------
298 : : * ExecSortMarkPos
299 : : *
300 : : * Calls tuplesort to save the current position in the sorted file.
301 : : * ----------------------------------------------------------------
302 : : */
303 : : void
304 : 356986 : ExecSortMarkPos(SortState *node)
305 : : {
306 : : /*
307 : : * if we haven't sorted yet, just return
308 : : */
309 [ - + ]: 356986 : if (!node->sort_Done)
310 : 0 : return;
311 : :
312 : 356986 : tuplesort_markpos((Tuplesortstate *) node->tuplesortstate);
313 : : }
314 : :
315 : : /* ----------------------------------------------------------------
316 : : * ExecSortRestrPos
317 : : *
318 : : * Calls tuplesort to restore the last saved sort file position.
319 : : * ----------------------------------------------------------------
320 : : */
321 : : void
322 : 24660 : ExecSortRestrPos(SortState *node)
323 : : {
324 : : /*
325 : : * if we haven't sorted yet, just return.
326 : : */
327 [ - + ]: 24660 : if (!node->sort_Done)
328 : 0 : return;
329 : :
330 : : /*
331 : : * restore the scan to the previously marked position
332 : : */
333 : 24660 : tuplesort_restorepos((Tuplesortstate *) node->tuplesortstate);
334 : : }
335 : :
336 : : void
337 : 28837 : ExecReScanSort(SortState *node)
338 : : {
339 : 28837 : PlanState *outerPlan = outerPlanState(node);
340 : :
341 : : /*
342 : : * If we haven't sorted yet, just return. If outerplan's chgParam is not
343 : : * NULL then it will be re-scanned by ExecProcNode, else no reason to
344 : : * re-scan it at all.
345 : : */
346 [ + + ]: 28837 : if (!node->sort_Done)
347 : 589 : return;
348 : :
349 : : /* must drop pointer to sort result tuple */
350 : 28248 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
351 : :
352 : : /*
353 : : * If subnode is to be rescanned then we forget previous sort results; we
354 : : * have to re-read the subplan and re-sort. Also must re-sort if the
355 : : * bounded-sort parameters changed or we didn't select randomAccess.
356 : : *
357 : : * Otherwise we can just rewind and rescan the sorted output.
358 : : */
359 [ + + ]: 28248 : if (outerPlan->chgParam != NULL ||
360 [ + - ]: 413 : node->bounded != node->bounded_Done ||
361 [ + + ]: 413 : node->bound != node->bound_Done ||
362 [ + + ]: 377 : !node->randomAccess)
363 : : {
364 : 28229 : node->sort_Done = false;
365 : 28229 : tuplesort_end((Tuplesortstate *) node->tuplesortstate);
366 : 28229 : node->tuplesortstate = NULL;
367 : :
368 : : /*
369 : : * if chgParam of subnode is not null then plan will be re-scanned by
370 : : * first ExecProcNode.
371 : : */
372 [ + + ]: 28229 : if (outerPlan->chgParam == NULL)
373 : 394 : ExecReScan(outerPlan);
374 : : }
375 : : else
376 : 19 : tuplesort_rescan((Tuplesortstate *) node->tuplesortstate);
377 : : }
378 : :
379 : : /* ----------------------------------------------------------------
380 : : * Parallel Query Support
381 : : * ----------------------------------------------------------------
382 : : */
383 : :
384 : : /* ----------------------------------------------------------------
385 : : * ExecSortEstimate
386 : : *
387 : : * Estimate space required to propagate sort statistics.
388 : : * ----------------------------------------------------------------
389 : : */
390 : : void
391 : 201 : ExecSortEstimate(SortState *node, ParallelContext *pcxt)
392 : : {
393 : : Size size;
394 : :
395 : : /* don't need this if not instrumenting or no workers */
396 [ + + - + ]: 201 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
397 : 193 : return;
398 : :
399 : 8 : size = mul_size(pcxt->nworkers, sizeof(TuplesortInstrumentation));
400 : 8 : size = add_size(size, offsetof(SharedSortInfo, sinstrument));
401 : 8 : shm_toc_estimate_chunk(&pcxt->estimator, size);
402 : 8 : shm_toc_estimate_keys(&pcxt->estimator, 1);
403 : : }
404 : :
405 : : /* ----------------------------------------------------------------
406 : : * ExecSortInitializeDSM
407 : : *
408 : : * Initialize DSM space for sort statistics.
409 : : * ----------------------------------------------------------------
410 : : */
411 : : void
412 : 201 : ExecSortInitializeDSM(SortState *node, ParallelContext *pcxt)
413 : : {
414 : : Size size;
415 : :
416 : : /* don't need this if not instrumenting or no workers */
417 [ + + - + ]: 201 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
418 : 193 : return;
419 : :
420 : 8 : size = offsetof(SharedSortInfo, sinstrument)
421 : 8 : + pcxt->nworkers * sizeof(TuplesortInstrumentation);
422 : 8 : node->shared_info = shm_toc_allocate(pcxt->toc, size);
423 : : /* ensure any unfilled slots will contain zeroes */
424 : 8 : memset(node->shared_info, 0, size);
425 : 8 : node->shared_info->num_workers = pcxt->nworkers;
426 : 8 : shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id,
427 : 8 : node->shared_info);
428 : : }
429 : :
430 : : /* ----------------------------------------------------------------
431 : : * ExecSortInitializeWorker
432 : : *
433 : : * Attach worker to DSM space for sort statistics.
434 : : * ----------------------------------------------------------------
435 : : */
436 : : void
437 : 499 : ExecSortInitializeWorker(SortState *node, ParallelWorkerContext *pwcxt)
438 : : {
439 : 499 : node->shared_info =
440 : 499 : shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, true);
441 : 499 : node->am_worker = true;
442 : 499 : }
443 : :
444 : : /* ----------------------------------------------------------------
445 : : * ExecSortRetrieveInstrumentation
446 : : *
447 : : * Transfer sort statistics from DSM to private memory.
448 : : * ----------------------------------------------------------------
449 : : */
450 : : void
451 : 8 : ExecSortRetrieveInstrumentation(SortState *node)
452 : : {
453 : : Size size;
454 : : SharedSortInfo *si;
455 : :
456 [ - + ]: 8 : if (node->shared_info == NULL)
457 : 0 : return;
458 : :
459 : 8 : size = offsetof(SharedSortInfo, sinstrument)
460 : 8 : + node->shared_info->num_workers * sizeof(TuplesortInstrumentation);
461 : 8 : si = palloc(size);
462 : 8 : memcpy(si, node->shared_info, size);
463 : 8 : node->shared_info = si;
464 : : }
|