Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 5220bb7

Browse files
committed
Expand run-time partition pruning to work with MergeAppend
This expands the support for the run-time partition pruning which was added for Append in 499be01 to also allow unneeded subnodes of a MergeAppend to be removed. Author: David Rowley Discussion: https://www.postgresql.org/message-id/CAKJS1f_F_V8D7Wu-HVdnH7zCUxhoGK8XhLLtd%3DCu85qDZzXrgg%40mail.gmail.com
1 parent b33ef39 commit 5220bb7

File tree

11 files changed

+356
-35
lines changed

11 files changed

+356
-35
lines changed

doc/src/sgml/perform.sgml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -899,12 +899,12 @@ EXPLAIN ANALYZE SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000
899899
Generally, the <command>EXPLAIN</command> output will display details for
900900
every plan node which was generated by the query planner. However, there
901901
are cases where the executor is able to determine that certain nodes are
902-
not required; currently, the only node type to support this is the
903-
<literal>Append</literal> node. This node type has the ability to discard
904-
subnodes which it is able to determine won't contain any records required
905-
by the query. It is possible to determine that nodes have been removed in
906-
this way by the presence of a "Subplans Removed" property in the
907-
<command>EXPLAIN</command> output.
902+
not required; currently, the only node types to support this are the
903+
<literal>Append</literal> and <literal>MergeAppend</literal> nodes. These
904+
node types have the ability to discard subnodes which they are able to
905+
determine won't contain any records required by the query. It is possible
906+
to determine that nodes have been removed in this way by the presence of a
907+
"Subplans Removed" property in the <command>EXPLAIN</command> output.
908908
</para>
909909
</sect2>
910910

src/backend/executor/nodeAppend.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
151151
/*
152152
* The case where no subplans survive pruning must be handled
153153
* specially. The problem here is that code in explain.c requires
154-
* an Append to have at least one subplan in order for it to
154+
* a MergeAppend to have at least one subplan in order for it to
155155
* properly determine the Vars in that subplan's targetlist. We
156156
* sidestep this issue by just initializing the first subplan and
157157
* setting as_whichplan to NO_MATCHING_SUBPLANS to indicate that

src/backend/executor/nodeMergeAppend.c

Lines changed: 116 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
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

293375
void
294376
ExecReScanMergeAppend(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];

src/backend/nodes/copyfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ _copyMergeAppend(const MergeAppend *from)
273273
COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid));
274274
COPY_POINTER_FIELD(collations, from->numCols * sizeof(Oid));
275275
COPY_POINTER_FIELD(nullsFirst, from->numCols * sizeof(bool));
276+
COPY_NODE_FIELD(part_prune_infos);
276277

277278
return newnode;
278279
}

src/backend/nodes/outfuncs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,8 @@ _outMergeAppend(StringInfo str, const MergeAppend *node)
434434
appendStringInfoString(str, " :nullsFirst");
435435
for (i = 0; i < node->numCols; i++)
436436
appendStringInfo(str, " %s", booltostr(node->nullsFirst[i]));
437+
438+
WRITE_NODE_FIELD(part_prune_infos);
437439
}
438440

439441
static void

src/backend/nodes/readfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,6 +1634,7 @@ _readMergeAppend(void)
16341634
READ_OID_ARRAY(sortOperators, local_node->numCols);
16351635
READ_OID_ARRAY(collations, local_node->numCols);
16361636
READ_BOOL_ARRAY(nullsFirst, local_node->numCols);
1637+
READ_NODE_FIELD(part_prune_infos);
16371638

16381639
READ_DONE();
16391640
}

src/backend/optimizer/plan/createplan.c

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,11 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
10681068
subplans = lappend(subplans, subplan);
10691069
}
10701070

1071+
/*
1072+
* If any quals exist, they may be useful to perform further partition
1073+
* pruning during execution. Gather information needed by the executor
1074+
* to do partition pruning.
1075+
*/
10711076
if (enable_partition_pruning &&
10721077
rel->reloptkind == RELOPT_BASEREL &&
10731078
best_path->partitioned_rels != NIL)
@@ -1078,7 +1083,6 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
10781083

10791084
if (best_path->path.param_info)
10801085
{
1081-
10821086
List *prmquals = best_path->path.param_info->ppi_clauses;
10831087

10841088
prmquals = extract_actual_clauses(prmquals, false);
@@ -1088,12 +1092,6 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
10881092
prunequal = list_concat(prunequal, prmquals);
10891093
}
10901094

1091-
/*
1092-
* If any quals exist, they may be useful to perform further partition
1093-
* pruning during execution. Generate a PartitionPruneInfo for each
1094-
* partitioned rel to store these quals and allow translation of
1095-
* partition indexes into subpath indexes.
1096-
*/
10971095
if (prunequal != NIL)
10981096
partpruneinfos =
10991097
make_partition_pruneinfo(root,
@@ -1133,6 +1131,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
11331131
List *pathkeys = best_path->path.pathkeys;
11341132
List *subplans = NIL;
11351133
ListCell *subpaths;
1134+
RelOptInfo *rel = best_path->path.parent;
1135+
List *partpruneinfos = NIL;
11361136

11371137
/*
11381138
* We don't have the actual creation of the MergeAppend node split out
@@ -1218,8 +1218,40 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
12181218
subplans = lappend(subplans, subplan);
12191219
}
12201220

1221+
/*
1222+
* If any quals exist, they may be useful to perform further partition
1223+
* pruning during execution. Gather information needed by the executor
1224+
* to do partition pruning.
1225+
*/
1226+
if (enable_partition_pruning &&
1227+
rel->reloptkind == RELOPT_BASEREL &&
1228+
best_path->partitioned_rels != NIL)
1229+
{
1230+
List *prunequal;
1231+
1232+
prunequal = extract_actual_clauses(rel->baserestrictinfo, false);
1233+
1234+
if (best_path->path.param_info)
1235+
{
1236+
1237+
List *prmquals = best_path->path.param_info->ppi_clauses;
1238+
1239+
prmquals = extract_actual_clauses(prmquals, false);
1240+
prmquals = (List *) replace_nestloop_params(root,
1241+
(Node *) prmquals);
1242+
1243+
prunequal = list_concat(prunequal, prmquals);
1244+
}
1245+
1246+
if (prunequal != NIL)
1247+
partpruneinfos = make_partition_pruneinfo(root,
1248+
best_path->partitioned_rels,
1249+
best_path->subpaths, prunequal);
1250+
}
1251+
12211252
node->partitioned_rels = best_path->partitioned_rels;
12221253
node->mergeplans = subplans;
1254+
node->part_prune_infos = partpruneinfos;
12231255

12241256
return (Plan *) node;
12251257
}

src/include/nodes/execnodes.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,12 @@ struct AppendState
11061106
* slots current output tuple of each subplan
11071107
* heap heap of active tuples
11081108
* initialized true if we have fetched first tuple from each subplan
1109+
* noopscan true if partition pruning proved that none of the
1110+
* mergeplans can contain a record to satisfy this query.
1111+
* prune_state details required to allow partitions to be
1112+
* eliminated from the scan, or NULL if not possible.
1113+
* valid_subplans for runtime pruning, valid mergeplans indexes to
1114+
* scan.
11091115
* ----------------
11101116
*/
11111117
typedef struct MergeAppendState
@@ -1118,6 +1124,9 @@ typedef struct MergeAppendState
11181124
TupleTableSlot **ms_slots; /* array of length ms_nplans */
11191125
struct binaryheap *ms_heap; /* binary heap of slot indices */
11201126
bool ms_initialized; /* are subplans started? */
1127+
bool ms_noopscan;
1128+
struct PartitionPruneState *ms_prune_state;
1129+
Bitmapset *ms_valid_subplans;
11211130
} MergeAppendState;
11221131

11231132
/* ----------------

src/include/nodes/plannodes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,9 @@ typedef struct MergeAppend
281281
Oid *sortOperators; /* OIDs of operators to sort them by */
282282
Oid *collations; /* OIDs of collations */
283283
bool *nullsFirst; /* NULLS FIRST/LAST directions */
284+
285+
/* Info for run-time subplan pruning, one entry per partitioned_rels */
286+
List *part_prune_infos; /* List of PartitionPruneInfo */
284287
} MergeAppend;
285288

286289
/* ----------------

0 commit comments

Comments
 (0)