PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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-2026, 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#include "utils/sortsupport.h"
47
48/*
49 * We have one slot for each item in the heap array. We use SlotNumber
50 * to store slot indexes. This doesn't actually provide any formal
51 * type-safety, but it makes the code more self-documenting.
52 */
54
56static int heap_compare_slots(Datum a, Datum b, void *arg);
57
58
59/* ----------------------------------------------------------------
60 * ExecInitMergeAppend
61 *
62 * Begin all of the subscans of the MergeAppend node.
63 * ----------------------------------------------------------------
64 */
66ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
67{
72 int nplans;
73 int i,
74 j;
75
76 /* check for unsupported flags */
78
79 /*
80 * create new MergeAppendState for our node
81 */
82 mergestate->ps.plan = (Plan *) node;
83 mergestate->ps.state = estate;
84 mergestate->ps.ExecProcNode = ExecMergeAppend;
85
86 /* If run-time partition pruning is enabled, then set that up now */
87 if (node->part_prune_index >= 0)
88 {
90
91 /*
92 * Set up pruning data structure. This also initializes the set of
93 * subplans to initialize (validsubplans) by taking into account the
94 * result of performing initial pruning if any.
95 */
98 node->part_prune_index,
99 node->apprelids,
101 mergestate->ms_prune_state = prunestate;
103
104 /*
105 * When no run-time pruning is required and there's at least one
106 * subplan, we can fill ms_valid_subplans immediately, preventing
107 * later calls to ExecFindMatchingSubPlans.
108 */
109 if (!prunestate->do_exec_prune && nplans > 0)
110 mergestate->ms_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
111 }
112 else
113 {
114 nplans = list_length(node->mergeplans);
115
116 /*
117 * When run-time partition pruning is not enabled we can just mark all
118 * subplans as valid; they must also all be initialized.
119 */
120 Assert(nplans > 0);
121 mergestate->ms_valid_subplans = validsubplans =
122 bms_add_range(NULL, 0, nplans - 1);
123 mergestate->ms_prune_state = NULL;
124 }
125
127 mergestate->mergeplans = mergeplanstates;
128 mergestate->ms_nplans = nplans;
129
130 mergestate->ms_slots = palloc0_array(TupleTableSlot *, nplans);
132 mergestate);
133
134 /*
135 * call ExecInitNode on each of the valid plans to be executed and save
136 * the results into the mergeplanstates array.
137 */
138 j = 0;
139 i = -1;
140 while ((i = bms_next_member(validsubplans, i)) >= 0)
141 {
142 Plan *initNode = (Plan *) list_nth(node->mergeplans, i);
143
144 mergeplanstates[j++] = ExecInitNode(initNode, estate, eflags);
145 }
146
147 /*
148 * Initialize MergeAppend's result tuple type and slot. If the child
149 * plans all produce the same fixed slot type, we can use that slot type;
150 * otherwise make a virtual slot. (Note that the result slot itself is
151 * used only to return a null tuple at end of execution; real tuples are
152 * returned to the caller in the children's own result slots. What we are
153 * doing here is allowing the parent plan node to optimize if the
154 * MergeAppend will return only one kind of slot.)
155 */
157 if (mergeops != NULL)
158 {
160 }
161 else
162 {
164 /* show that the output slot type is not fixed */
165 mergestate->ps.resultopsset = true;
166 mergestate->ps.resultopsfixed = false;
167 }
168
169 /*
170 * Miscellaneous initialization
171 */
172 mergestate->ps.ps_ProjInfo = NULL;
173
174 /*
175 * initialize sort-key information
176 */
177 mergestate->ms_nkeys = node->numCols;
178 mergestate->ms_sortkeys = palloc0_array(SortSupportData, node->numCols);
179
180 for (i = 0; i < node->numCols; i++)
181 {
182 SortSupport sortKey = mergestate->ms_sortkeys + i;
183
185 sortKey->ssup_collation = node->collations[i];
186 sortKey->ssup_nulls_first = node->nullsFirst[i];
187 sortKey->ssup_attno = node->sortColIdx[i];
188
189 /*
190 * It isn't feasible to perform abbreviated key conversion, since
191 * tuples are pulled into mergestate's binary heap as needed. It
192 * would likely be counter-productive to convert tuples into an
193 * abbreviated representation as they're pulled up, so opt out of that
194 * additional optimization entirely.
195 */
196 sortKey->abbreviate = false;
197
198 PrepareSortSupportFromOrderingOp(node->sortOperators[i], sortKey);
199 }
200
201 /*
202 * initialize to show we have not run the subplans yet
203 */
204 mergestate->ms_initialized = false;
205
206 return mergestate;
207}
208
209/* ----------------------------------------------------------------
210 * ExecMergeAppend
211 *
212 * Handles iteration over multiple subplans.
213 * ----------------------------------------------------------------
214 */
215static TupleTableSlot *
217{
219 TupleTableSlot *result;
221
223
224 if (!node->ms_initialized)
225 {
226 /* Nothing to do if all subplans were pruned */
227 if (node->ms_nplans == 0)
229
230 /*
231 * If we've yet to determine the valid subplans then do so now. If
232 * run-time pruning is disabled then the valid subplans will always be
233 * set to all subplans.
234 */
235 if (node->ms_valid_subplans == NULL)
236 node->ms_valid_subplans =
238
239 /*
240 * First time through: pull the first tuple from each valid subplan,
241 * and set up the heap.
242 */
243 i = -1;
244 while ((i = bms_next_member(node->ms_valid_subplans, i)) >= 0)
245 {
246 node->ms_slots[i] = ExecProcNode(node->mergeplans[i]);
247 if (!TupIsNull(node->ms_slots[i]))
249 }
251 node->ms_initialized = true;
252 }
253 else
254 {
255 /*
256 * Otherwise, pull the next tuple from whichever subplan we returned
257 * from last time, and reinsert the subplan index into the heap,
258 * because it might now compare differently against the existing
259 * elements of the heap. (We could perhaps simplify the logic a bit
260 * by doing this before returning from the prior call, but it's better
261 * to not pull tuples until necessary.)
262 */
264 node->ms_slots[i] = ExecProcNode(node->mergeplans[i]);
265 if (!TupIsNull(node->ms_slots[i]))
267 else
268 (void) binaryheap_remove_first(node->ms_heap);
269 }
270
271 if (binaryheap_empty(node->ms_heap))
272 {
273 /* All the subplans are exhausted, and so is the heap */
274 result = ExecClearTuple(node->ps.ps_ResultTupleSlot);
275 }
276 else
277 {
279 result = node->ms_slots[i];
280 }
281
282 return result;
283}
284
285/*
286 * Compare the tuples in the two given slots.
287 */
288static int32
290{
294
297 int nkey;
298
301
302 for (nkey = 0; nkey < node->ms_nkeys; nkey++)
303 {
306 Datum datum1,
307 datum2;
308 bool isNull1,
309 isNull2;
310 int compare;
311
312 datum1 = slot_getattr(s1, attno, &isNull1);
313 datum2 = slot_getattr(s2, attno, &isNull2);
314
317 sortKey);
318 if (compare != 0)
319 {
321 return compare;
322 }
323 }
324 return 0;
325}
326
327/* ----------------------------------------------------------------
328 * ExecEndMergeAppend
329 *
330 * Shuts down the subscans of the MergeAppend node.
331 *
332 * Returns nothing of interest.
333 * ----------------------------------------------------------------
334 */
335void
337{
338 PlanState **mergeplans;
339 int nplans;
340 int i;
341
342 /*
343 * get information from the node
344 */
345 mergeplans = node->mergeplans;
346 nplans = node->ms_nplans;
347
348 /*
349 * shut down each of the subscans
350 */
351 for (i = 0; i < nplans; i++)
352 ExecEndNode(mergeplans[i]);
353}
354
355void
357{
358 int i;
359
360 /*
361 * If any PARAM_EXEC Params used in pruning expressions have changed, then
362 * we'd better unset the valid subplans so that they are reselected for
363 * the new parameter values.
364 */
365 if (node->ms_prune_state &&
366 bms_overlap(node->ps.chgParam,
368 {
370 node->ms_valid_subplans = NULL;
371 }
372
373 for (i = 0; i < node->ms_nplans; i++)
374 {
375 PlanState *subnode = node->mergeplans[i];
376
377 /*
378 * ExecReScan doesn't know about my subplans, so I have to do
379 * changed-parameter signaling myself.
380 */
381 if (node->ps.chgParam != NULL)
383
384 /*
385 * If chgParam of subnode is not null then plan will be re-scanned by
386 * first ExecProcNode.
387 */
388 if (subnode->chgParam == NULL)
390 }
392 node->ms_initialized = false;
393}
int16 AttrNumber
Definition attnum.h:21
void binaryheap_build(binaryheap *heap)
Definition binaryheap.c:136
void binaryheap_replace_first(binaryheap *heap, bh_node_type d)
Definition binaryheap.c:253
void binaryheap_reset(binaryheap *heap)
Definition binaryheap.c:61
bh_node_type binaryheap_first(binaryheap *heap)
Definition binaryheap.c:175
bh_node_type binaryheap_remove_first(binaryheap *heap)
Definition binaryheap.c:190
void binaryheap_add_unordered(binaryheap *heap, bh_node_type d)
Definition binaryheap.c:114
binaryheap * binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
Definition binaryheap.c:37
#define binaryheap_empty(h)
Definition binaryheap.h:65
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1290
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition bitmapset.c:1003
void bms_free(Bitmapset *a)
Definition bitmapset.c:239
int bms_num_members(const Bitmapset *a)
Definition bitmapset.c:744
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:575
#define INVERT_COMPARE_RESULT(var)
Definition c.h:1195
#define Assert(condition)
Definition c.h:945
int32_t int32
Definition c.h:614
Datum arg
Definition elog.c:1322
void ExecReScan(PlanState *node)
Definition execAmi.c:78
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)
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
const TupleTableSlotOps TTSOpsVirtual
Definition execTuples.c:84
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
const TupleTableSlotOps * ExecGetCommonSlotOps(PlanState **planstates, int nplans)
Definition execUtils.c:541
void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg)
Definition execUtils.c:915
#define EXEC_FLAG_BACKWARD
Definition executor.h:70
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition executor.h:315
#define EXEC_FLAG_MARK
Definition executor.h:71
#define palloc_array(type, count)
Definition fe_memutils.h:76
#define palloc0_array(type, count)
Definition fe_memutils.h:77
static int compare(const void *arg1, const void *arg2)
Definition geqo_pool.c:144
int b
Definition isn.c:74
int a
Definition isn.c:73
int j
Definition isn.c:78
int i
Definition isn.c:77
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:123
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:161
#define castNode(_type_, nodeptr)
Definition nodes.h:182
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
uint64_t Datum
Definition postgres.h:70
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212
static int32 DatumGetInt32(Datum X)
Definition postgres.h:202
static int fb(int x)
char * s1
char * s2
void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup)
static int ApplySortComparator(Datum datum1, bool isNull1, Datum datum2, bool isNull2, SortSupport ssup)
PlanState ** mergeplans
Definition execnodes.h:1553
SortSupport ms_sortkeys
Definition execnodes.h:1556
Bitmapset * ms_valid_subplans
Definition execnodes.h:1561
struct binaryheap * ms_heap
Definition execnodes.h:1558
TupleTableSlot ** ms_slots
Definition execnodes.h:1557
struct PartitionPruneState * ms_prune_state
Definition execnodes.h:1560
int part_prune_index
Definition plannodes.h:464
Bitmapset * apprelids
Definition plannodes.h:434
List * mergeplans
Definition plannodes.h:440
Bitmapset * execparamids
Bitmapset * chgParam
Definition execnodes.h:1209
TupleTableSlot * ps_ResultTupleSlot
Definition execnodes.h:1215
AttrNumber ssup_attno
Definition sortsupport.h:81
MemoryContext ssup_cxt
Definition sortsupport.h:66
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition tuptable.h:417
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
#define TupIsNull(slot)
Definition tuptable.h:325