Skip to content

Commit 5ac9c43

Browse files
committed
Cleanup partition pruning step generation
There was some code in gen_prune_steps_from_opexps that needlessly checked a list was not empty when it clearly had to contain at least one item. This prompted a further cleanup operation in partprune.c. Additionally, the previous code could end up adding additional needless INTERSECT steps. However, those do not appear to be able to cause any misbehavior. gen_prune_steps_from_opexps is now no longer in charge of generating combine pruning steps. Instead, gen_partprune_steps_internal, which already does some combine step creation has been given the sole responsibility of generating all combine steps. This means that when we recursively call gen_partprune_steps_internal, since it always now adds a combine step when it produces multiple steps, we can just pay attention to the final step returned. In passing, do quite a bit of work on the comments to try to more clearly explain the role of both gen_partprune_steps_internal and gen_prune_steps_from_opexps. This is fairly complex code so some extra effort to give any new readers an overview of how things work seems like a good idea. Author: Amit Langote Reported-by: Andy Fan Reviewed-by: Kyotaro Horiguchi, Andy Fan, Ryan Lambert, David Rowley Discussion: https://postgr.es/m/CAKU4AWqWoVii+bRTeBQmeVW+PznkdO8DfbwqNsu9Gj4ubt9A6w@mail.gmail.com
1 parent 7e3c541 commit 5ac9c43

File tree

3 files changed

+84
-87
lines changed

3 files changed

+84
-87
lines changed

src/backend/partitioning/partbounds.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -4067,7 +4067,7 @@ get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
40674067
if (!list_has_null)
40684068
{
40694069
/*
4070-
* Gin up a "col IS NOT NULL" test that will be AND'd with the main
4070+
* Gin up a "col IS NOT NULL" test that will be ANDed with the main
40714071
* expression. This might seem redundant, but the partition routing
40724072
* machinery needs it.
40734073
*/

src/backend/partitioning/partprune.c

+82-85
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ static PartitionPruneStep *gen_prune_step_op(GeneratePruningStepsContext *contex
156156
static PartitionPruneStep *gen_prune_step_combine(GeneratePruningStepsContext *context,
157157
List *source_stepids,
158158
PartitionPruneCombineOp combineOp);
159-
static PartitionPruneStep *gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
160-
List **keyclauses, Bitmapset *nullkeys);
159+
static List *gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
160+
List **keyclauses, Bitmapset *nullkeys);
161161
static PartClauseMatchStatus match_clause_to_partition_key(GeneratePruningStepsContext *context,
162162
Expr *clause, Expr *partkey, int partkeyidx,
163163
bool *clause_is_not_null,
@@ -924,22 +924,34 @@ get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
924924

925925
/*
926926
* gen_partprune_steps_internal
927-
* Processes 'clauses' to generate partition pruning steps.
928-
*
929-
* From OpExpr clauses that are mutually AND'd, we find combinations of those
930-
* that match to the partition key columns and for every such combination,
931-
* we emit a PartitionPruneStepOp containing a vector of expressions whose
932-
* values are used as a look up key to search partitions by comparing the
933-
* values with partition bounds. Relevant details of the operator and a
934-
* vector of (possibly cross-type) comparison functions is also included with
935-
* each step.
936-
*
937-
* For BoolExpr clauses, we recursively generate steps for each argument, and
938-
* return a PartitionPruneStepCombine of their results.
939-
*
940-
* The return value is a list of the steps generated, which are also added to
941-
* the context's steps list. Each step is assigned a step identifier, unique
942-
* even across recursive calls.
927+
* Processes 'clauses' to generate a List of partition pruning steps. We
928+
* return NIL when no steps were generated.
929+
*
930+
* These partition pruning steps come in 2 forms; operator steps and combine
931+
* steps.
932+
*
933+
* Operator steps (PartitionPruneStepOp) contain details of clauses that we
934+
* determined that we can use for partition pruning. These contain details of
935+
* the expression which is being compared to the partition key and the
936+
* comparison function.
937+
*
938+
* Combine steps (PartitionPruneStepCombine) instruct the partition pruning
939+
* code how it should produce a single set of partitions from multiple input
940+
* operator and other combine steps. A PARTPRUNE_COMBINE_INTERSECT type
941+
* combine step will merge its input steps to produce a result which only
942+
* contains the partitions which are present in all of the input operator
943+
* steps. A PARTPRUNE_COMBINE_UNION combine step will produce a result that
944+
* has all of the partitions from each of the input operator steps.
945+
*
946+
* For BoolExpr clauses, each argument is processed recursively. Steps
947+
* generated from processing an OR BoolExpr will be combined using
948+
* PARTPRUNE_COMBINE_UNION. AND BoolExprs get combined using
949+
* PARTPRUNE_COMBINE_INTERSECT.
950+
*
951+
* Otherwise, the list of clauses we receive we assume to be mutually ANDed.
952+
* We generate all of the pruning steps we can based on these clauses and then
953+
* at the end, if we have more than 1 step, we combine each step with a
954+
* PARTPRUNE_COMBINE_INTERSECT combine step. Single steps are returned as-is.
943955
*
944956
* If we find clauses that are mutually contradictory, or contradictory with
945957
* the partitioning constraint, or a pseudoconstant clause that contains
@@ -1046,11 +1058,16 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
10461058

10471059
if (argsteps != NIL)
10481060
{
1049-
PartitionPruneStep *step;
1061+
/*
1062+
* gen_partprune_steps_internal() always adds a single
1063+
* combine step when it generates multiple steps, so
1064+
* here we can just pay attention to the last one in
1065+
* the list. If it just generated one, then the last
1066+
* one in the list is still the one we want.
1067+
*/
1068+
PartitionPruneStep *last = llast(argsteps);
10501069

1051-
Assert(list_length(argsteps) == 1);
1052-
step = (PartitionPruneStep *) linitial(argsteps);
1053-
arg_stepids = lappend_int(arg_stepids, step->step_id);
1070+
arg_stepids = lappend_int(arg_stepids, last->step_id);
10541071
}
10551072
else
10561073
{
@@ -1089,9 +1106,7 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
10891106
else if (is_andclause(clause))
10901107
{
10911108
List *args = ((BoolExpr *) clause)->args;
1092-
List *argsteps,
1093-
*arg_stepids = NIL;
1094-
ListCell *lc1;
1109+
List *argsteps;
10951110

10961111
/*
10971112
* args may itself contain clauses of arbitrary type, so just
@@ -1104,21 +1119,16 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
11041119
if (context->contradictory)
11051120
return NIL;
11061121

1107-
foreach(lc1, argsteps)
1108-
{
1109-
PartitionPruneStep *step = lfirst(lc1);
1110-
1111-
arg_stepids = lappend_int(arg_stepids, step->step_id);
1112-
}
1113-
1114-
if (arg_stepids != NIL)
1115-
{
1116-
PartitionPruneStep *step;
1122+
/*
1123+
* gen_partprune_steps_internal() always adds a single combine
1124+
* step when it generates multiple steps, so here we can just
1125+
* pay attention to the last one in the list. If it just
1126+
* generated one, then the last one in the list is still the
1127+
* one we want.
1128+
*/
1129+
if (argsteps != NIL)
1130+
result = lappend(result, llast(argsteps));
11171131

1118-
step = gen_prune_step_combine(context, arg_stepids,
1119-
PARTPRUNE_COMBINE_INTERSECT);
1120-
result = lappend(result, step);
1121-
}
11221132
continue;
11231133
}
11241134

@@ -1253,12 +1263,11 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
12531263
}
12541264
else if (generate_opsteps)
12551265
{
1256-
PartitionPruneStep *step;
1266+
List *opsteps;
12571267

12581268
/* Strategy 2 */
1259-
step = gen_prune_steps_from_opexps(context, keyclauses, nullkeys);
1260-
if (step != NULL)
1261-
result = lappend(result, step);
1269+
opsteps = gen_prune_steps_from_opexps(context, keyclauses, nullkeys);
1270+
result = list_concat(result, opsteps);
12621271
}
12631272
else if (bms_num_members(notnullkeys) == part_scheme->partnatts)
12641273
{
@@ -1271,12 +1280,14 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
12711280
}
12721281

12731282
/*
1274-
* Finally, results from all entries appearing in result should be
1275-
* combined using an INTERSECT combine step, if more than one.
1283+
* Finally, if there are multiple steps, since the 'clauses' are mutually
1284+
* ANDed, add an INTERSECT step to combine the partition sets resulting
1285+
* from them and append it to the result list.
12761286
*/
12771287
if (list_length(result) > 1)
12781288
{
12791289
List *step_ids = NIL;
1290+
PartitionPruneStep *final;
12801291

12811292
foreach(lc, result)
12821293
{
@@ -1285,14 +1296,9 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
12851296
step_ids = lappend_int(step_ids, step->step_id);
12861297
}
12871298

1288-
if (step_ids != NIL)
1289-
{
1290-
PartitionPruneStep *step;
1291-
1292-
step = gen_prune_step_combine(context, step_ids,
1293-
PARTPRUNE_COMBINE_INTERSECT);
1294-
result = lappend(result, step);
1295-
}
1299+
final = gen_prune_step_combine(context, step_ids,
1300+
PARTPRUNE_COMBINE_INTERSECT);
1301+
result = lappend(result, final);
12961302
}
12971303

12981304
return result;
@@ -1356,15 +1362,26 @@ gen_prune_step_combine(GeneratePruningStepsContext *context,
13561362

13571363
/*
13581364
* gen_prune_steps_from_opexps
1359-
* Generate pruning steps based on clauses for partition keys
1360-
*
1361-
* 'keyclauses' contains one list of clauses per partition key. We check here
1362-
* if we have found clauses for a valid subset of the partition key. In some
1363-
* cases, (depending on the type of partitioning being used) if we didn't
1364-
* find clauses for a given key, we discard clauses that may have been
1365-
* found for any subsequent keys; see specific notes below.
1365+
* Generate and return a list of PartitionPruneStepOp that are based on
1366+
* OpExpr and BooleanTest clauses that have been matched to the partition
1367+
* key.
1368+
*
1369+
* 'keyclauses' is an array of List pointers, indexed by the partition key's
1370+
* index. Each List element in the array can contain clauses that match to
1371+
* the corresponding partition key column. Partition key columns without any
1372+
* matched clauses will have an empty List.
1373+
*
1374+
* Some partitioning strategies allow pruning to still occur when we only have
1375+
* clauses for a prefix of the partition key columns, for example, RANGE
1376+
* partitioning. Other strategies, such as HASH partitioning, require clauses
1377+
* for all partition key columns.
1378+
*
1379+
* When we return multiple pruning steps here, it's up to the caller to add a
1380+
* relevant "combine" step to combine the returned steps. This is not done
1381+
* here as callers may wish to include additional pruning steps before
1382+
* combining them all.
13661383
*/
1367-
static PartitionPruneStep *
1384+
static List *
13681385
gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
13691386
List **keyclauses, Bitmapset *nullkeys)
13701387
{
@@ -1397,7 +1414,7 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
13971414
*/
13981415
if (part_scheme->strategy == PARTITION_STRATEGY_HASH &&
13991416
clauselist == NIL && !bms_is_member(i, nullkeys))
1400-
return NULL;
1417+
return NIL;
14011418

14021419
foreach(lc, clauselist)
14031420
{
@@ -1728,27 +1745,7 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
17281745
break;
17291746
}
17301747

1731-
/* Lastly, add a combine step to mutually AND these op steps, if needed */
1732-
if (list_length(opsteps) > 1)
1733-
{
1734-
List *opstep_ids = NIL;
1735-
1736-
foreach(lc, opsteps)
1737-
{
1738-
PartitionPruneStep *step = lfirst(lc);
1739-
1740-
opstep_ids = lappend_int(opstep_ids, step->step_id);
1741-
}
1742-
1743-
if (opstep_ids != NIL)
1744-
return gen_prune_step_combine(context, opstep_ids,
1745-
PARTPRUNE_COMBINE_INTERSECT);
1746-
return NULL;
1747-
}
1748-
else if (opsteps != NIL)
1749-
return linitial(opsteps);
1750-
1751-
return NULL;
1748+
return opsteps;
17521749
}
17531750

17541751
/*
@@ -1782,8 +1779,8 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
17821779
* true otherwise.
17831780
*
17841781
* * PARTCLAUSE_MATCH_STEPS if there is a match.
1785-
* Output arguments: *clause_steps is set to a list of PartitionPruneStep
1786-
* generated for the clause.
1782+
* Output arguments: *clause_steps is set to the list of recursively
1783+
* generated steps for the clause.
17871784
*
17881785
* * PARTCLAUSE_MATCH_CONTRADICT if the clause is self-contradictory, ie
17891786
* it provably returns FALSE or NULL.

src/include/nodes/plannodes.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1215,7 +1215,7 @@ typedef struct PartitionPruneStep
12151215
} PartitionPruneStep;
12161216

12171217
/*
1218-
* PartitionPruneStepOp - Information to prune using a set of mutually AND'd
1218+
* PartitionPruneStepOp - Information to prune using a set of mutually ANDed
12191219
* OpExpr clauses
12201220
*
12211221
* This contains information extracted from up to partnatts OpExpr clauses,

0 commit comments

Comments
 (0)