3939#include "postgres.h"
4040
4141#include "executor/execdebug.h"
42+ #include "executor/execPartition.h"
4243#include "executor/nodeMergeAppend.h"
4344#include "lib/binaryheap.h"
4445#include "miscadmin.h"
@@ -65,8 +66,10 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
6566{
6667 MergeAppendState * mergestate = makeNode (MergeAppendState );
6768 PlanState * * mergeplanstates ;
69+ Bitmapset * validsubplans ;
6870 int nplans ;
69- int i ;
71+ int i ,
72+ j ;
7073 ListCell * lc ;
7174
7275 /* check for unsupported flags */
@@ -78,19 +81,80 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
7881 */
7982 ExecLockNonLeafAppendTables (node -> partitioned_rels , estate );
8083
81- /*
82- * Set up empty vector of subplan states
83- */
84- nplans = list_length (node -> mergeplans );
85-
86- mergeplanstates = (PlanState * * ) palloc0 (nplans * sizeof (PlanState * ));
87-
8884 /*
8985 * create new MergeAppendState for our node
9086 */
9187 mergestate -> ps .plan = (Plan * ) node ;
9288 mergestate -> ps .state = estate ;
9389 mergestate -> ps .ExecProcNode = ExecMergeAppend ;
90+ mergestate -> ms_noopscan = false;
91+
92+ /* If run-time partition pruning is enabled, then set that up now */
93+ if (node -> part_prune_infos != NIL )
94+ {
95+ PartitionPruneState * prunestate ;
96+
97+ /* We may need an expression context to evaluate partition exprs */
98+ ExecAssignExprContext (estate , & mergestate -> ps );
99+
100+ prunestate = ExecCreatePartitionPruneState (& mergestate -> ps ,
101+ node -> part_prune_infos );
102+ mergestate -> ms_prune_state = prunestate ;
103+
104+ /* Perform an initial partition prune, if required. */
105+ if (prunestate -> do_initial_prune )
106+ {
107+ /* Determine which subplans survive initial pruning */
108+ validsubplans = ExecFindInitialMatchingSubPlans (prunestate ,
109+ list_length (node -> mergeplans ));
110+
111+ /*
112+ * The case where no subplans survive pruning must be handled
113+ * specially. The problem here is that code in explain.c requires
114+ * an Append to have at least one subplan in order for it to
115+ * properly determine the Vars in that subplan's targetlist. We
116+ * sidestep this issue by just initializing the first subplan and
117+ * setting ms_noopscan to true to indicate that we don't really
118+ * need to scan any subnodes.
119+ */
120+ if (bms_is_empty (validsubplans ))
121+ {
122+ mergestate -> ms_noopscan = true;
123+
124+ /* Mark the first as valid so that it's initialized below */
125+ validsubplans = bms_make_singleton (0 );
126+ }
127+
128+ nplans = bms_num_members (validsubplans );
129+ }
130+ else
131+ {
132+ /* We'll need to initialize all subplans */
133+ nplans = list_length (node -> mergeplans );
134+ validsubplans = bms_add_range (NULL , 0 , nplans - 1 );
135+ }
136+
137+ /*
138+ * If no runtime pruning is required, we can fill ms_valid_subplans
139+ * immediately, preventing later calls to ExecFindMatchingSubPlans.
140+ */
141+ if (!prunestate -> do_exec_prune )
142+ mergestate -> ms_valid_subplans = bms_add_range (NULL , 0 , nplans - 1 );
143+ }
144+ else
145+ {
146+ nplans = list_length (node -> mergeplans );
147+
148+ /*
149+ * When run-time partition pruning is not enabled we can just mark all
150+ * subplans as valid; they must also all be initialized.
151+ */
152+ mergestate -> ms_valid_subplans = validsubplans =
153+ bms_add_range (NULL , 0 , nplans - 1 );
154+ mergestate -> ms_prune_state = NULL ;
155+ }
156+
157+ mergeplanstates = (PlanState * * ) palloc (nplans * sizeof (PlanState * ));
94158 mergestate -> mergeplans = mergeplanstates ;
95159 mergestate -> ms_nplans = nplans ;
96160
@@ -101,26 +165,24 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
101165 /*
102166 * Miscellaneous initialization
103167 *
104- * MergeAppend plans don't have expression contexts because they never
105- * call ExecQual or ExecProject.
106- */
107-
108- /*
109168 * MergeAppend nodes do have Result slots, which hold pointers to tuples,
110169 * so we have to initialize them.
111170 */
112171 ExecInitResultTupleSlotTL (estate , & mergestate -> ps );
113172
114173 /*
115- * call ExecInitNode on each of the plans to be executed and save the
116- * results into the array "mergeplans" .
174+ * call ExecInitNode on each of the valid plans to be executed and save
175+ * the results into the mergeplanstates array .
117176 */
118- i = 0 ;
177+ j = i = 0 ;
119178 foreach (lc , node -> mergeplans )
120179 {
121- Plan * initNode = (Plan * ) lfirst (lc );
180+ if (bms_is_member (i , validsubplans ))
181+ {
182+ Plan * initNode = (Plan * ) lfirst (lc );
122183
123- mergeplanstates [i ] = ExecInitNode (initNode , estate , eflags );
184+ mergeplanstates [j ++ ] = ExecInitNode (initNode , estate , eflags );
185+ }
124186 i ++ ;
125187 }
126188
@@ -178,11 +240,25 @@ ExecMergeAppend(PlanState *pstate)
178240
179241 if (!node -> ms_initialized )
180242 {
243+ /* Nothing to do if all subplans were pruned */
244+ if (node -> ms_noopscan )
245+ return ExecClearTuple (node -> ps .ps_ResultTupleSlot );
246+
181247 /*
182- * First time through: pull the first tuple from each subplan, and set
183- * up the heap.
248+ * If we've yet to determine the valid subplans then do so now. If
249+ * run-time pruning is disabled then the valid subplans will always be
250+ * set to all subplans.
184251 */
185- for (i = 0 ; i < node -> ms_nplans ; i ++ )
252+ if (node -> ms_valid_subplans == NULL )
253+ node -> ms_valid_subplans =
254+ ExecFindMatchingSubPlans (node -> ms_prune_state );
255+
256+ /*
257+ * First time through: pull the first tuple from each valid subplan,
258+ * and set up the heap.
259+ */
260+ i = -1 ;
261+ while ((i = bms_next_member (node -> ms_valid_subplans , i )) >= 0 )
186262 {
187263 node -> ms_slots [i ] = ExecProcNode (node -> mergeplans [i ]);
188264 if (!TupIsNull (node -> ms_slots [i ]))
@@ -288,13 +364,32 @@ ExecEndMergeAppend(MergeAppendState *node)
288364 */
289365 for (i = 0 ; i < nplans ; i ++ )
290366 ExecEndNode (mergeplans [i ]);
367+
368+ /*
369+ * release any resources associated with run-time pruning
370+ */
371+ if (node -> ms_prune_state )
372+ ExecDestroyPartitionPruneState (node -> ms_prune_state );
291373}
292374
293375void
294376ExecReScanMergeAppend (MergeAppendState * node )
295377{
296378 int i ;
297379
380+ /*
381+ * If any PARAM_EXEC Params used in pruning expressions have changed, then
382+ * we'd better unset the valid subplans so that they are reselected for
383+ * the new parameter values.
384+ */
385+ if (node -> ms_prune_state &&
386+ bms_overlap (node -> ps .chgParam ,
387+ node -> ms_prune_state -> execparamids ))
388+ {
389+ bms_free (node -> ms_valid_subplans );
390+ node -> ms_valid_subplans = NULL ;
391+ }
392+
298393 for (i = 0 ; i < node -> ms_nplans ; i ++ )
299394 {
300395 PlanState * subnode = node -> mergeplans [i ];
0 commit comments