PostgreSQL Source Code git master
nodeMergeAppend.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * nodeMergeAppend.c
4 * routines to handle MergeAppend nodes.
5 *
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/executor/nodeMergeAppend.c
12 *
13 *-------------------------------------------------------------------------
14 */
15/* INTERFACE ROUTINES
16 * ExecInitMergeAppend - initialize the MergeAppend node
17 * ExecMergeAppend - retrieve the next tuple from the node
18 * ExecEndMergeAppend - shut down the MergeAppend node
19 * ExecReScanMergeAppend - rescan the MergeAppend node
20 *
21 * NOTES
22 * A MergeAppend node contains a list of one or more subplans.
23 * These are each expected to deliver tuples that are sorted according
24 * to a common sort key. The MergeAppend node merges these streams
25 * to produce output sorted the same way.
26 *
27 * MergeAppend nodes don't make use of their left and right
28 * subtrees, rather they maintain a list of subplans so
29 * a typical MergeAppend node looks like this in the plan tree:
30 *
31 * ...
32 * /
33 * MergeAppend---+------+------+--- nil
34 * / \ | | |
35 * nil nil ... ... ...
36 * subplans
37 */
38
39#include "postgres.h"
40
41#include "executor/executor.h"
44#include "lib/binaryheap.h"
45#include "miscadmin.h"
46
47/*
48 * We have one slot for each item in the heap array. We use SlotNumber
49 * to store slot indexes. This doesn't actually provide any formal
50 * type-safety, but it makes the code more self-documenting.
51 */
53
55static int heap_compare_slots(Datum a, Datum b, void *arg);
56
57
58/* ----------------------------------------------------------------
59 * ExecInitMergeAppend
60 *
61 * Begin all of the subscans of the MergeAppend node.
62 * ----------------------------------------------------------------
63 */
65ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
66{
68 PlanState **mergeplanstates;
69 const TupleTableSlotOps *mergeops;
70 Bitmapset *validsubplans;
71 int nplans;
72 int i,
73 j;
74
75 /* check for unsupported flags */
77
78 /*
79 * create new MergeAppendState for our node
80 */
81 mergestate->ps.plan = (Plan *) node;
82 mergestate->ps.state = estate;
83 mergestate->ps.ExecProcNode = ExecMergeAppend;
84
85 /* If run-time partition pruning is enabled, then set that up now */
86 if (node->part_prune_index >= 0)
87 {
88 PartitionPruneState *prunestate;
89
90 /*
91 * Set up pruning data structure. This also initializes the set of
92 * subplans to initialize (validsubplans) by taking into account the
93 * result of performing initial pruning if any.
94 */
95 prunestate = ExecInitPartitionExecPruning(&mergestate->ps,
97 node->part_prune_index,
98 node->apprelids,
99 &validsubplans);
100 mergestate->ms_prune_state = prunestate;
101 nplans = bms_num_members(validsubplans);
102
103 /*
104 * When no run-time pruning is required and there's at least one
105 * subplan, we can fill ms_valid_subplans immediately, preventing
106 * later calls to ExecFindMatchingSubPlans.
107 */
108 if (!prunestate->do_exec_prune && nplans > 0)
109 mergestate->ms_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
110 }
111 else
112 {
113 nplans = list_length(node->mergeplans);
114
115 /*
116 * When run-time partition pruning is not enabled we can just mark all
117 * subplans as valid; they must also all be initialized.
118 */
119 Assert(nplans > 0);
120 mergestate->ms_valid_subplans = validsubplans =
121 bms_add_range(NULL, 0, nplans - 1);
122 mergestate->ms_prune_state = NULL;
123 }
124
125 mergeplanstates = (PlanState **) palloc(nplans * sizeof(PlanState *));
126 mergestate->mergeplans = mergeplanstates;
127 mergestate->ms_nplans = nplans;
128
129 mergestate->ms_slots = (TupleTableSlot **) palloc0(sizeof(TupleTableSlot *) * nplans);
130 mergestate->ms_heap = binaryheap_allocate(nplans, heap_compare_slots,
131 mergestate);
132
133 /*
134 * call ExecInitNode on each of the valid plans to be executed and save
135 * the results into the mergeplanstates array.
136 */
137 j = 0;
138 i = -1;
139 while ((i = bms_next_member(validsubplans, i)) >= 0)
140 {
141 Plan *initNode = (Plan *) list_nth(node->mergeplans, i);
142
143 mergeplanstates[j++] = ExecInitNode(initNode, estate, eflags);
144 }
145
146 /*
147 * Initialize MergeAppend's result tuple type and slot. If the child
148 * plans all produce the same fixed slot type, we can use that slot type;
149 * otherwise make a virtual slot. (Note that the result slot itself is
150 * used only to return a null tuple at end of execution; real tuples are
151 * returned to the caller in the children's own result slots. What we are
152 * doing here is allowing the parent plan node to optimize if the
153 * MergeAppend will return only one kind of slot.)
154 */
155 mergeops = ExecGetCommonSlotOps(mergeplanstates, j);
156 if (mergeops != NULL)
157 {
158 ExecInitResultTupleSlotTL(&mergestate->ps, mergeops);
159 }
160 else
161 {
163 /* show that the output slot type is not fixed */
164 mergestate->ps.resultopsset = true;
165 mergestate->ps.resultopsfixed = false;
166 }
167
168 /*
169 * Miscellaneous initialization
170 */
171 mergestate->ps.ps_ProjInfo = NULL;
172
173 /*
174 * initialize sort-key information
175 */
176 mergestate->ms_nkeys = node->numCols;
177 mergestate->ms_sortkeys = palloc0(sizeof(SortSupportData) * node->numCols);
178
179 for (i = 0; i < node->numCols; i++)
180 {
181 SortSupport sortKey = mergestate->ms_sortkeys + i;
182
184 sortKey->ssup_collation = node->collations[i];
185 sortKey->ssup_nulls_first = node->nullsFirst[i];
186 sortKey->ssup_attno = node->sortColIdx[i];
187
188 /*
189 * It isn't feasible to perform abbreviated key conversion, since
190 * tuples are pulled into mergestate's binary heap as needed. It
191 * would likely be counter-productive to convert tuples into an
192 * abbreviated representation as they're pulled up, so opt out of that
193 * additional optimization entirely.
194 */
195 sortKey->abbreviate = false;
196
197 PrepareSortSupportFromOrderingOp(node->sortOperators[i], sortKey);
198 }
199
200 /*
201 * initialize to show we have not run the subplans yet
202 */
203 mergestate->ms_initialized = false;
204
205 return mergestate;
206}
207
208/* ----------------------------------------------------------------
209 * ExecMergeAppend
210 *
211 * Handles iteration over multiple subplans.
212 * ----------------------------------------------------------------
213 */
214static TupleTableSlot *
216{
218 TupleTableSlot *result;
220
222
223 if (!node->ms_initialized)
224 {
225 /* Nothing to do if all subplans were pruned */
226 if (node->ms_nplans == 0)
228
229 /*
230 * If we've yet to determine the valid subplans then do so now. If
231 * run-time pruning is disabled then the valid subplans will always be
232 * set to all subplans.
233 */
234 if (node->ms_valid_subplans == NULL)
235 node->ms_valid_subplans =
236 ExecFindMatchingSubPlans(node->ms_prune_state, false, NULL);
237
238 /*
239 * First time through: pull the first tuple from each valid subplan,
240 * and set up the heap.
241 */
242 i = -1;
243 while ((i = bms_next_member(node->ms_valid_subplans, i)) >= 0)
244 {
245 node->ms_slots[i] = ExecProcNode(node->mergeplans[i]);
246 if (!TupIsNull(node->ms_slots[i]))
248 }
250 node->ms_initialized = true;
251 }
252 else
253 {
254 /*
255 * Otherwise, pull the next tuple from whichever subplan we returned
256 * from last time, and reinsert the subplan index into the heap,
257 * because it might now compare differently against the existing
258 * elements of the heap. (We could perhaps simplify the logic a bit
259 * by doing this before returning from the prior call, but it's better
260 * to not pull tuples until necessary.)
261 */
263 node->ms_slots[i] = ExecProcNode(node->mergeplans[i]);
264 if (!TupIsNull(node->ms_slots[i]))
266 else
267 (void) binaryheap_remove_first(node->ms_heap);
268 }
269
270 if (binaryheap_empty(node->ms_heap))
271 {
272 /* All the subplans are exhausted, and so is the heap */
273 result = ExecClearTuple(node->ps.ps_ResultTupleSlot);
274 }
275 else
276 {
278 result = node->ms_slots[i];
279 }
280
281 return result;
282}
283
284/*
285 * Compare the tuples in the two given slots.
286 */
287static int32
289{
291 SlotNumber slot1 = DatumGetInt32(a);
292 SlotNumber slot2 = DatumGetInt32(b);
293
294 TupleTableSlot *s1 = node->ms_slots[slot1];
295 TupleTableSlot *s2 = node->ms_slots[slot2];
296 int nkey;
297
300
301 for (nkey = 0; nkey < node->ms_nkeys; nkey++)
302 {
303 SortSupport sortKey = node->ms_sortkeys + nkey;
304 AttrNumber attno = sortKey->ssup_attno;
305 Datum datum1,
306 datum2;
307 bool isNull1,
308 isNull2;
309 int compare;
310
311 datum1 = slot_getattr(s1, attno, &isNull1);
312 datum2 = slot_getattr(s2, attno, &isNull2);
313
314 compare = ApplySortComparator(datum1, isNull1,
315 datum2, isNull2,
316 sortKey);
317 if (compare != 0)
318 {
320 return compare;
321 }
322 }
323 return 0;
324}
325
326/* ----------------------------------------------------------------
327 * ExecEndMergeAppend
328 *
329 * Shuts down the subscans of the MergeAppend node.
330 *
331 * Returns nothing of interest.
332 * ----------------------------------------------------------------
333 */
334void
336{
337 PlanState **mergeplans;
338 int nplans;
339 int i;
340
341 /*
342 * get information from the node
343 */
344 mergeplans = node->mergeplans;
345 nplans = node->ms_nplans;
346
347 /*
348 * shut down each of the subscans
349 */
350 for (i = 0; i < nplans; i++)
351 ExecEndNode(mergeplans[i]);
352}
353
354void
356{
357 int i;
358
359 /*
360 * If any PARAM_EXEC Params used in pruning expressions have changed, then
361 * we'd better unset the valid subplans so that they are reselected for
362 * the new parameter values.
363 */
364 if (node->ms_prune_state &&
365 bms_overlap(node->ps.chgParam,
367 {
369 node->ms_valid_subplans = NULL;
370 }
371
372 for (i = 0; i < node->ms_nplans; i++)
373 {
374 PlanState *subnode = node->mergeplans[i];
375
376 /*
377 * ExecReScan doesn't know about my subplans, so I have to do
378 * changed-parameter signaling myself.
379 */
380 if (node->ps.chgParam != NULL)
381 UpdateChangedParamSet(subnode, node->ps.chgParam);
382
383 /*
384 * If chgParam of subnode is not null then plan will be re-scanned by
385 * first ExecProcNode.
386 */
387 if (subnode->chgParam == NULL)
388 ExecReScan(subnode);
389 }
391 node->ms_initialized = false;
392}
int16 AttrNumber
Definition: attnum.h:21
void binaryheap_build(binaryheap *heap)
Definition: binaryheap.c:138
void binaryheap_replace_first(binaryheap *heap, bh_node_type d)
Definition: binaryheap.c:255
void binaryheap_reset(binaryheap *heap)
Definition: binaryheap.c:63
bh_node_type binaryheap_first(binaryheap *heap)
Definition: binaryheap.c:177
bh_node_type binaryheap_remove_first(binaryheap *heap)
Definition: binaryheap.c:192
void binaryheap_add_unordered(binaryheap *heap, bh_node_type d)
Definition: binaryheap.c:116
binaryheap * binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
Definition: binaryheap.c:39
#define binaryheap_empty(h)
Definition: binaryheap.h:65
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:1019
void bms_free(Bitmapset *a)
Definition: bitmapset.c:239
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:751
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:582
#define INVERT_COMPARE_RESULT(var)
Definition: c.h:1063
#define Assert(condition)
Definition: c.h:815
int32_t int32
Definition: c.h:484
void ExecReScan(PlanState *node)
Definition: execAmi.c:76
PartitionPruneState * ExecInitPartitionExecPruning(PlanState *planstate, int n_total_subplans, int part_prune_index, Bitmapset *relids, Bitmapset **initially_valid_subplans)
Bitmapset * ExecFindMatchingSubPlans(PartitionPruneState *prunestate, bool initial_prune, Bitmapset **validsubplan_rtis)
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:562
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:142
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1986
const TupleTableSlotOps * ExecGetCommonSlotOps(PlanState **planstates, int nplans)
Definition: execUtils.c:537
void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg)
Definition: execUtils.c:900
#define EXEC_FLAG_BACKWARD
Definition: executor.h:68
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:267
#define EXEC_FLAG_MARK
Definition: executor.h:69
static int compare(const void *arg1, const void *arg2)
Definition: geqo_pool.c:145
int b
Definition: isn.c:69
int a
Definition: isn.c:68
int j
Definition: isn.c:73
int i
Definition: isn.c:72
void * palloc0(Size size)
Definition: mcxt.c:1347
void * palloc(Size size)
Definition: mcxt.c:1317
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
int32 SlotNumber
static int heap_compare_slots(Datum a, Datum b, void *arg)
static TupleTableSlot * ExecMergeAppend(PlanState *pstate)
void ExecReScanMergeAppend(MergeAppendState *node)
int32 SlotNumber
MergeAppendState * ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
void ExecEndMergeAppend(MergeAppendState *node)
#define makeNode(_type_)
Definition: nodes.h:155
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
void * arg
static int list_length(const List *l)
Definition: pg_list.h:152
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
uintptr_t Datum
Definition: postgres.h:69
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:217
static int32 DatumGetInt32(Datum X)
Definition: postgres.h:207
char * s1
char * s2
void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup)
Definition: sortsupport.c:134
static int ApplySortComparator(Datum datum1, bool isNull1, Datum datum2, bool isNull2, SortSupport ssup)
Definition: sortsupport.h:200
PlanState ** mergeplans
Definition: execnodes.h:1523
SortSupport ms_sortkeys
Definition: execnodes.h:1526
Bitmapset * ms_valid_subplans
Definition: execnodes.h:1531
PlanState ps
Definition: execnodes.h:1522
struct binaryheap * ms_heap
Definition: execnodes.h:1528
TupleTableSlot ** ms_slots
Definition: execnodes.h:1527
struct PartitionPruneState * ms_prune_state
Definition: execnodes.h:1530
int part_prune_index
Definition: plannodes.h:397
Bitmapset * apprelids
Definition: plannodes.h:371
List * mergeplans
Definition: plannodes.h:373
Bitmapset * execparamids
bool resultopsset
Definition: execnodes.h:1235
Plan * plan
Definition: execnodes.h:1150
EState * state
Definition: execnodes.h:1152
Bitmapset * chgParam
Definition: execnodes.h:1182
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1188
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:1190
bool resultopsfixed
Definition: execnodes.h:1231
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1156
AttrNumber ssup_attno
Definition: sortsupport.h:81
bool ssup_nulls_first
Definition: sortsupport.h:75
MemoryContext ssup_cxt
Definition: sortsupport.h:66
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:395
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:454
#define TupIsNull(slot)
Definition: tuptable.h:306