1 /*-------------------------------------------------------------------------
2 *
3 * partprune.c
4 * Support for partition pruning during query planning and execution
5 *
6 * This module implements partition pruning using the information contained in
7 * a table's partition descriptor, query clauses, and run-time parameters.
8 *
9 * During planning, clauses that can be matched to the table's partition key
10 * are turned into a set of "pruning steps", which are then executed to
11 * identify a set of partitions (as indexes in the RelOptInfo->part_rels
12 * array) that satisfy the constraints in the step. Partitions not in the set
13 * are said to have been pruned.
14 *
15 * A base pruning step may involve expressions whose values are only known
16 * during execution, such as Params, in which case pruning cannot occur
17 * entirely during planning. In that case, such steps are included alongside
18 * the plan, so that they can be used by the executor for further pruning.
19 *
20 * There are two kinds of pruning steps. A "base" pruning step represents
21 * tests on partition key column(s), typically comparisons to expressions.
22 * A "combine" pruning step represents a Boolean connector (AND/OR), and
23 * combines the outputs of some previous steps using the appropriate
24 * combination method.
25 *
26 * See gen_partprune_steps_internal() for more details on step generation.
27 *
28 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
29 * Portions Copyright (c) 1994, Regents of the University of California
30 *
31 * IDENTIFICATION
32 * src/backend/partitioning/partprune.c
33 *
34 *-------------------------------------------------------------------------
35 */
36 #include "postgres.h"
37
38 #include "access/hash.h"
39 #include "access/nbtree.h"
40 #include "catalog/pg_operator.h"
41 #include "catalog/pg_opfamily.h"
42 #include "catalog/pg_proc.h"
43 #include "catalog/pg_type.h"
44 #include "executor/executor.h"
45 #include "miscadmin.h"
46 #include "nodes/makefuncs.h"
47 #include "nodes/nodeFuncs.h"
48 #include "optimizer/clauses.h"
49 #include "optimizer/pathnode.h"
50 #include "optimizer/planner.h"
51 #include "optimizer/predtest.h"
52 #include "optimizer/prep.h"
53 #include "optimizer/var.h"
54 #include "partitioning/partprune.h"
55 #include "partitioning/partbounds.h"
56 #include "rewrite/rewriteManip.h"
57 #include "utils/lsyscache.h"
58
59
60 /*
61 * Information about a clause matched with a partition key.
62 */
63 typedef struct PartClauseInfo
64 {
65 int keyno; /* Partition key number (0 to partnatts - 1) */
66 Oid opno; /* operator used to compare partkey to expr */
67 bool op_is_ne; /* is clause's original operator <> ? */
68 Expr *expr; /* expr the partition key is compared to */
69 Oid cmpfn; /* Oid of function to compare 'expr' to the
70 * partition key */
71 int op_strategy; /* btree strategy identifying the operator */
72 } PartClauseInfo;
73
74 /*
75 * PartClauseMatchStatus
76 * Describes the result of match_clause_to_partition_key()
77 */
78 typedef enum PartClauseMatchStatus
79 {
80 PARTCLAUSE_NOMATCH,
81 PARTCLAUSE_MATCH_CLAUSE,
82 PARTCLAUSE_MATCH_NULLNESS,
83 PARTCLAUSE_MATCH_STEPS,
84 PARTCLAUSE_MATCH_CONTRADICT,
85 PARTCLAUSE_UNSUPPORTED
86 } PartClauseMatchStatus;
87
88 /*
89 * PartClauseTarget
90 * Identifies which qual clauses we can use for generating pruning steps
91 */
92 typedef enum PartClauseTarget
93 {
94 PARTTARGET_PLANNER, /* want to prune during planning */
95 PARTTARGET_INITIAL, /* want to prune during executor startup */
96 PARTTARGET_EXEC /* want to prune during each plan node scan */
97 } PartClauseTarget;
98
99 /*
100 * GeneratePruningStepsContext
101 * Information about the current state of generation of "pruning steps"
102 * for a given set of clauses
103 *
104 * gen_partprune_steps() initializes and returns an instance of this struct.
105 *
106 * Note that has_mutable_op, has_mutable_arg, and has_exec_param are set if
107 * we found any potentially-useful-for-pruning clause having those properties,
108 * whether or not we actually used the clause in the steps list. This
109 * definition allows us to skip the PARTTARGET_EXEC pass in some cases.
110 */
111 typedef struct GeneratePruningStepsContext
112 {
113 /* Copies of input arguments for gen_partprune_steps: */
114 RelOptInfo *rel; /* the partitioned relation */
115 PartClauseTarget target; /* use-case we're generating steps for */
116 /* Result data: */
117 List *steps; /* list of PartitionPruneSteps */
118 bool has_mutable_op; /* clauses include any stable operators */
119 bool has_mutable_arg; /* clauses include any mutable comparison
120 * values, *other than* exec params */
121 bool has_exec_param; /* clauses include any PARAM_EXEC params */
122 bool contradictory; /* clauses were proven self-contradictory */
123 /* Working state: */
124 int next_step_id;
125 } GeneratePruningStepsContext;
126
127 /* The result of performing one PartitionPruneStep */
128 typedef struct PruneStepResult
129 {
130 /*
131 * The offsets of bounds (in a table's boundinfo) whose partition is
132 * selected by the pruning step.
133 */
134 Bitmapset *bound_offsets;
135
136 bool scan_default; /* Scan the default partition? */
137 bool scan_null; /* Scan the partition for NULL values? */
138 } PruneStepResult;
139
140
141 static List *make_partitionedrel_pruneinfo(PlannerInfo *root,
142 RelOptInfo *parentrel,
143 int *relid_subplan_map,
144 List *partitioned_rels, List *prunequal,
145 Bitmapset **matchedsubplans);
146 static void gen_partprune_steps(RelOptInfo *rel, List *clauses,
147 PartClauseTarget target,
148 GeneratePruningStepsContext *context);
149 static List *gen_partprune_steps_internal(GeneratePruningStepsContext *context,
150 List *clauses);
151 static PartitionPruneStep *gen_prune_step_op(GeneratePruningStepsContext *context,
152 StrategyNumber opstrategy, bool op_is_ne,
153 List *exprs, List *cmpfns, Bitmapset *nullkeys);
154 static PartitionPruneStep *gen_prune_step_combine(GeneratePruningStepsContext *context,
155 List *source_stepids,
156 PartitionPruneCombineOp combineOp);
157 static PartitionPruneStep *gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
158 List **keyclauses, Bitmapset *nullkeys);
159 static PartClauseMatchStatus match_clause_to_partition_key(GeneratePruningStepsContext *context,
160 Expr *clause, Expr *partkey, int partkeyidx,
161 bool *clause_is_not_null,
162 PartClauseInfo **pc, List **clause_steps);
163 static List *get_steps_using_prefix(GeneratePruningStepsContext *context,
164 StrategyNumber step_opstrategy,
165 bool step_op_is_ne,
166 Expr *step_lastexpr,
167 Oid step_lastcmpfn,
168 int step_lastkeyno,
169 Bitmapset *step_nullkeys,
170 List *prefix);
171 static List *get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
172 StrategyNumber step_opstrategy,
173 bool step_op_is_ne,
174 Expr *step_lastexpr,
175 Oid step_lastcmpfn,
176 int step_lastkeyno,
177 Bitmapset *step_nullkeys,
178 ListCell *start,
179 List *step_exprs,
180 List *step_cmpfns);
181 static PruneStepResult *get_matching_hash_bounds(PartitionPruneContext *context,
182 StrategyNumber opstrategy, Datum *values, int nvalues,
183 FmgrInfo *partsupfunc, Bitmapset *nullkeys);
184 static PruneStepResult *get_matching_list_bounds(PartitionPruneContext *context,
185 StrategyNumber opstrategy, Datum value, int nvalues,
186 FmgrInfo *partsupfunc, Bitmapset *nullkeys);
187 static PruneStepResult *get_matching_range_bounds(PartitionPruneContext *context,
188 StrategyNumber opstrategy, Datum *values, int nvalues,
189 FmgrInfo *partsupfunc, Bitmapset *nullkeys);
190 static Bitmapset *pull_exec_paramids(Expr *expr);
191 static bool pull_exec_paramids_walker(Node *node, Bitmapset **context);
192 static Bitmapset *get_partkey_exec_paramids(List *steps);
193 static PruneStepResult *perform_pruning_base_step(PartitionPruneContext *context,
194 PartitionPruneStepOp *opstep);
195 static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *context,
196 PartitionPruneStepCombine *cstep,
197 PruneStepResult **step_results);
198 static PartClauseMatchStatus match_boolean_partition_clause(Oid partopfamily,
199 Expr *clause,
200 Expr *partkey,
201 Expr **outconst);
202 static void partkey_datum_from_expr(PartitionPruneContext *context,
203 Expr *expr, int stateidx,
204 Datum *value, bool *isnull);
205
206
207 /*
208 * make_partition_pruneinfo
209 * Builds a PartitionPruneInfo which can be used in the executor to allow
210 * additional partition pruning to take place. Returns NULL when
211 * partition pruning would be useless.
212 *
213 * 'parentrel' is the RelOptInfo for an appendrel, and 'subpaths' is the list
214 * of scan paths for its child rels.
215 *
216 * 'partitioned_rels' is a List containing Lists of relids of partitioned
217 * tables (a/k/a non-leaf partitions) that are parents of some of the child
218 * rels. Here we attempt to populate the PartitionPruneInfo by adding a
219 * 'prune_infos' item for each sublist in the 'partitioned_rels' list.
220 * However, some of the sets of partitioned relations may not require any
221 * run-time pruning. In these cases we'll simply not include a 'prune_infos'
222 * item for that set and instead we'll add all the subplans which belong to
223 * that set into the PartitionPruneInfo's 'other_subplans' field. Callers
224 * will likely never want to prune subplans which are mentioned in this field.
225 *
226 * 'prunequal' is a list of potential pruning quals.
227 */
228 PartitionPruneInfo *
make_partition_pruneinfo(PlannerInfo * root,RelOptInfo * parentrel,List * subpaths,List * partitioned_rels,List * prunequal)229 make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
230 List *subpaths, List *partitioned_rels,
231 List *prunequal)
232 {
233 PartitionPruneInfo *pruneinfo;
234 Bitmapset *allmatchedsubplans = NULL;
235 int *relid_subplan_map;
236 ListCell *lc;
237 List *prunerelinfos;
238 int i;
239
240 /*
241 * Construct a temporary array to map from planner relids to subplan
242 * indexes. For convenience, we use 1-based indexes here, so that zero
243 * can represent an un-filled array entry.
244 */
245 relid_subplan_map = palloc0(sizeof(int) * root->simple_rel_array_size);
246
247 /*
248 * relid_subplan_map maps relid of a leaf partition to the index in
249 * 'subpaths' of the scan plan for that partition.
250 */
251 i = 1;
252 foreach(lc, subpaths)
253 {
254 Path *path = (Path *) lfirst(lc);
255 RelOptInfo *pathrel = path->parent;
256
257 Assert(IS_SIMPLE_REL(pathrel));
258 Assert(pathrel->relid < root->simple_rel_array_size);
259 /* No duplicates please */
260 Assert(relid_subplan_map[pathrel->relid] == 0);
261
262 relid_subplan_map[pathrel->relid] = i++;
263 }
264
265 /* We now build a PartitionedRelPruneInfo for each partitioned rel. */
266 prunerelinfos = NIL;
267 foreach(lc, partitioned_rels)
268 {
269 List *rels = (List *) lfirst(lc);
270 List *pinfolist;
271 Bitmapset *matchedsubplans = NULL;
272
273 pinfolist = make_partitionedrel_pruneinfo(root, parentrel,
274 relid_subplan_map,
275 rels, prunequal,
276 &matchedsubplans);
277
278 /* When pruning is possible, record the matched subplans */
279 if (pinfolist != NIL)
280 {
281 prunerelinfos = lappend(prunerelinfos, pinfolist);
282 allmatchedsubplans = bms_join(matchedsubplans,
283 allmatchedsubplans);
284 }
285 }
286
287 pfree(relid_subplan_map);
288
289 /*
290 * If none of the partition hierarchies had any useful run-time pruning
291 * quals, then we can just not bother with run-time pruning.
292 */
293 if (prunerelinfos == NIL)
294 return NULL;
295
296 /* Else build the result data structure */
297 pruneinfo = makeNode(PartitionPruneInfo);
298 pruneinfo->prune_infos = prunerelinfos;
299
300 /*
301 * Some subplans may not belong to any of the listed partitioned rels.
302 * This can happen for UNION ALL queries which include a non-partitioned
303 * table, or when some of the hierarchies aren't run-time prunable. Build
304 * a bitmapset of the indexes of all such subplans, so that the executor
305 * can identify which subplans should never be pruned.
306 */
307 if (bms_num_members(allmatchedsubplans) < list_length(subpaths))
308 {
309 Bitmapset *other_subplans;
310
311 /* Create the complement of allmatchedsubplans */
312 other_subplans = bms_add_range(NULL, 0, list_length(subpaths) - 1);
313 other_subplans = bms_del_members(other_subplans, allmatchedsubplans);
314
315 pruneinfo->other_subplans = other_subplans;
316 }
317 else
318 pruneinfo->other_subplans = NULL;
319
320 return pruneinfo;
321 }
322
323 /*
324 * make_partitionedrel_pruneinfo
325 * Build a List of PartitionedRelPruneInfos, one for each partitioned
326 * rel. These can be used in the executor to allow additional partition
327 * pruning to take place.
328 *
329 * Here we generate partition pruning steps for 'prunequal' and also build a
330 * data structure which allows mapping of partition indexes into 'subpaths'
331 * indexes.
332 *
333 * If no non-Const expressions are being compared to the partition key in any
334 * of the 'partitioned_rels', then we return NIL to indicate no run-time
335 * pruning should be performed. Run-time pruning would be useless since the
336 * pruning done during planning will have pruned everything that can be.
337 *
338 * On non-NIL return, 'matchedsubplans' is set to the subplan indexes which
339 * were matched to this partition hierarchy.
340 */
341 static List *
make_partitionedrel_pruneinfo(PlannerInfo * root,RelOptInfo * parentrel,int * relid_subplan_map,List * partitioned_rels,List * prunequal,Bitmapset ** matchedsubplans)342 make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
343 int *relid_subplan_map,
344 List *partitioned_rels, List *prunequal,
345 Bitmapset **matchedsubplans)
346 {
347 RelOptInfo *targetpart = NULL;
348 List *pinfolist = NIL;
349 bool doruntimeprune = false;
350 int *relid_subpart_map;
351 Bitmapset *subplansfound = NULL;
352 ListCell *lc;
353 int i;
354
355 /*
356 * Construct a temporary array to map from planner relids to index of the
357 * partitioned_rel. For convenience, we use 1-based indexes here, so that
358 * zero can represent an un-filled array entry.
359 */
360 relid_subpart_map = palloc0(sizeof(int) * root->simple_rel_array_size);
361
362 /*
363 * relid_subpart_map maps relid of a non-leaf partition to the index in
364 * 'partitioned_rels' of that rel (which will also be the index in the
365 * returned PartitionedRelPruneInfo list of the info for that partition).
366 */
367 i = 1;
368 foreach(lc, partitioned_rels)
369 {
370 Index rti = lfirst_int(lc);
371
372 Assert(rti < root->simple_rel_array_size);
373 /* No duplicates please */
374 Assert(relid_subpart_map[rti] == 0);
375
376 relid_subpart_map[rti] = i++;
377 }
378
379 /* We now build a PartitionedRelPruneInfo for each partitioned rel */
380 foreach(lc, partitioned_rels)
381 {
382 Index rti = lfirst_int(lc);
383 RelOptInfo *subpart = find_base_rel(root, rti);
384 PartitionedRelPruneInfo *pinfo;
385 RangeTblEntry *rte;
386 Bitmapset *present_parts;
387 int nparts = subpart->nparts;
388 int *subplan_map;
389 int *subpart_map;
390 List *partprunequal;
391 List *initial_pruning_steps;
392 List *exec_pruning_steps;
393 Bitmapset *execparamids;
394 GeneratePruningStepsContext context;
395
396 /*
397 * The first item in the list is the target partitioned relation.
398 */
399 if (!targetpart)
400 {
401 targetpart = subpart;
402
403 /*
404 * The prunequal is presented to us as a qual for 'parentrel'.
405 * Frequently this rel is the same as targetpart, so we can skip
406 * an adjust_appendrel_attrs step. But it might not be, and then
407 * we have to translate. We update the prunequal parameter here,
408 * because in later iterations of the loop for child partitions,
409 * we want to translate from parent to child variables.
410 */
411 if (!bms_equal(parentrel->relids, subpart->relids))
412 {
413 int nappinfos;
414 AppendRelInfo **appinfos = find_appinfos_by_relids(root,
415 subpart->relids,
416 &nappinfos);
417
418 prunequal = (List *) adjust_appendrel_attrs(root, (Node *)
419 prunequal,
420 nappinfos,
421 appinfos);
422
423 pfree(appinfos);
424 }
425
426 partprunequal = prunequal;
427 }
428 else
429 {
430 /*
431 * For sub-partitioned tables the columns may not be in the same
432 * order as the parent, so we must translate the prunequal to make
433 * it compatible with this relation.
434 */
435 partprunequal = (List *)
436 adjust_appendrel_attrs_multilevel(root,
437 (Node *) prunequal,
438 subpart->relids,
439 targetpart->relids);
440 }
441
442 /*
443 * Convert pruning qual to pruning steps. We may need to do this
444 * twice, once to obtain executor startup pruning steps, and once for
445 * executor per-scan pruning steps. This first pass creates startup
446 * pruning steps and detects whether there's any possibly-useful quals
447 * that would require per-scan pruning.
448 */
449 gen_partprune_steps(subpart, partprunequal, PARTTARGET_INITIAL,
450 &context);
451
452 if (context.contradictory)
453 {
454 /*
455 * This shouldn't happen as the planner should have detected this
456 * earlier. However, we do use additional quals from parameterized
457 * paths here. These do only compare Params to the partition key,
458 * so this shouldn't cause the discovery of any new qual
459 * contradictions that were not previously discovered as the Param
460 * values are unknown during planning. Anyway, we'd better do
461 * something sane here, so let's just disable run-time pruning.
462 */
463 return NIL;
464 }
465
466 /*
467 * If no mutable operators or expressions appear in usable pruning
468 * clauses, then there's no point in running startup pruning, because
469 * plan-time pruning should have pruned everything prunable.
470 */
471 if (context.has_mutable_op || context.has_mutable_arg)
472 initial_pruning_steps = context.steps;
473 else
474 initial_pruning_steps = NIL;
475
476 /*
477 * If no exec Params appear in potentially-usable pruning clauses,
478 * then there's no point in even thinking about per-scan pruning.
479 */
480 if (context.has_exec_param)
481 {
482 /* ... OK, we'd better think about it */
483 gen_partprune_steps(subpart, partprunequal, PARTTARGET_EXEC,
484 &context);
485
486 if (context.contradictory)
487 {
488 /* As above, skip run-time pruning if anything fishy happens */
489 return NIL;
490 }
491
492 exec_pruning_steps = context.steps;
493
494 /*
495 * Detect which exec Params actually got used; the fact that some
496 * were in available clauses doesn't mean we actually used them.
497 * Skip per-scan pruning if there are none.
498 */
499 execparamids = get_partkey_exec_paramids(exec_pruning_steps);
500
501 if (bms_is_empty(execparamids))
502 exec_pruning_steps = NIL;
503 }
504 else
505 {
506 /* No exec Params anywhere, so forget about scan-time pruning */
507 exec_pruning_steps = NIL;
508 execparamids = NULL;
509 }
510
511 if (initial_pruning_steps || exec_pruning_steps)
512 doruntimeprune = true;
513
514 /*
515 * Construct the subplan and subpart maps for this partitioning level.
516 * Here we convert to zero-based indexes, with -1 for empty entries.
517 * Also construct a Bitmapset of all partitions that are present (that
518 * is, not pruned already).
519 */
520 subplan_map = (int *) palloc(nparts * sizeof(int));
521 subpart_map = (int *) palloc(nparts * sizeof(int));
522 present_parts = NULL;
523
524 for (i = 0; i < nparts; i++)
525 {
526 RelOptInfo *partrel = subpart->part_rels[i];
527 int subplanidx = relid_subplan_map[partrel->relid] - 1;
528 int subpartidx = relid_subpart_map[partrel->relid] - 1;
529
530 subplan_map[i] = subplanidx;
531 subpart_map[i] = subpartidx;
532 if (subplanidx >= 0)
533 {
534 present_parts = bms_add_member(present_parts, i);
535
536 /* Record finding this subplan */
537 subplansfound = bms_add_member(subplansfound, subplanidx);
538 }
539 else if (subpartidx >= 0)
540 present_parts = bms_add_member(present_parts, i);
541 }
542
543 rte = root->simple_rte_array[subpart->relid];
544
545 pinfo = makeNode(PartitionedRelPruneInfo);
546 pinfo->reloid = rte->relid;
547 pinfo->pruning_steps = NIL; /* not used */
548 pinfo->present_parts = present_parts;
549 pinfo->nparts = nparts;
550 pinfo->nexprs = 0; /* not used */
551 pinfo->subplan_map = subplan_map;
552 pinfo->subpart_map = subpart_map;
553 pinfo->hasexecparam = NULL; /* not used */
554 pinfo->do_initial_prune = (initial_pruning_steps != NIL);
555 pinfo->do_exec_prune = (exec_pruning_steps != NIL);
556 pinfo->execparamids = execparamids;
557 pinfo->initial_pruning_steps = initial_pruning_steps;
558 pinfo->exec_pruning_steps = exec_pruning_steps;
559
560 pinfolist = lappend(pinfolist, pinfo);
561 }
562
563 pfree(relid_subpart_map);
564
565 if (!doruntimeprune)
566 {
567 /* No run-time pruning required. */
568 return NIL;
569 }
570
571 *matchedsubplans = subplansfound;
572
573 return pinfolist;
574 }
575
576 /*
577 * gen_partprune_steps
578 * Process 'clauses' (typically a rel's baserestrictinfo list of clauses)
579 * and create a list of "partition pruning steps".
580 *
581 * 'target' tells whether to generate pruning steps for planning (use
582 * immutable clauses only), or for executor startup (use any allowable
583 * clause except ones containing PARAM_EXEC Params), or for executor
584 * per-scan pruning (use any allowable clause).
585 *
586 * 'context' is an output argument that receives the steps list as well as
587 * some subsidiary flags; see the GeneratePruningStepsContext typedef.
588 */
589 static void
gen_partprune_steps(RelOptInfo * rel,List * clauses,PartClauseTarget target,GeneratePruningStepsContext * context)590 gen_partprune_steps(RelOptInfo *rel, List *clauses, PartClauseTarget target,
591 GeneratePruningStepsContext *context)
592 {
593 /* Initialize all output values to zero/false/NULL */
594 memset(context, 0, sizeof(GeneratePruningStepsContext));
595 context->rel = rel;
596 context->target = target;
597
598 /*
599 * For sub-partitioned tables there's a corner case where if the
600 * sub-partitioned table shares any partition keys with its parent, then
601 * it's possible that the partitioning hierarchy allows the parent
602 * partition to only contain a narrower range of values than the
603 * sub-partitioned table does. In this case it is possible that we'd
604 * include partitions that could not possibly have any tuples matching
605 * 'clauses'. The possibility of such a partition arrangement is perhaps
606 * unlikely for non-default partitions, but it may be more likely in the
607 * case of default partitions, so we'll add the parent partition table's
608 * partition qual to the clause list in this case only. This may result
609 * in the default partition being eliminated.
610 */
611 if (partition_bound_has_default(rel->boundinfo) &&
612 rel->partition_qual != NIL)
613 {
614 List *partqual = rel->partition_qual;
615
616 partqual = (List *) expression_planner((Expr *) partqual);
617
618 /* Fix Vars to have the desired varno */
619 if (rel->relid != 1)
620 ChangeVarNodes((Node *) partqual, 1, rel->relid, 0);
621
622 /* Use list_copy to avoid modifying the passed-in List */
623 clauses = list_concat(list_copy(clauses), partqual);
624 }
625
626 /* Down into the rabbit-hole. */
627 (void) gen_partprune_steps_internal(context, clauses);
628 }
629
630 /*
631 * prune_append_rel_partitions
632 * Process rel's baserestrictinfo and make use of quals which can be
633 * evaluated during query planning in order to determine the minimum set
634 * of partitions which must be scanned to satisfy these quals. Returns
635 * the matching partitions in the form of a Relids set containing the
636 * partitions' RT indexes.
637 *
638 * Callers must ensure that 'rel' is a partitioned table.
639 */
640 Relids
prune_append_rel_partitions(RelOptInfo * rel)641 prune_append_rel_partitions(RelOptInfo *rel)
642 {
643 Relids result;
644 List *clauses = rel->baserestrictinfo;
645 List *pruning_steps;
646 GeneratePruningStepsContext gcontext;
647 PartitionPruneContext context;
648 Bitmapset *partindexes;
649 int i;
650
651 Assert(clauses != NIL);
652 Assert(rel->part_scheme != NULL);
653
654 /* If there are no partitions, return the empty set */
655 if (rel->nparts == 0)
656 return NULL;
657
658 /*
659 * Process clauses to extract pruning steps that are usable at plan time.
660 * If the clauses are found to be contradictory, we can return the empty
661 * set.
662 */
663 gen_partprune_steps(rel, clauses, PARTTARGET_PLANNER,
664 &gcontext);
665 if (gcontext.contradictory)
666 return NULL;
667 pruning_steps = gcontext.steps;
668
669 /* Set up PartitionPruneContext */
670 context.strategy = rel->part_scheme->strategy;
671 context.partnatts = rel->part_scheme->partnatts;
672 context.nparts = rel->nparts;
673 context.boundinfo = rel->boundinfo;
674 context.partcollation = rel->part_scheme->partcollation;
675 context.partsupfunc = rel->part_scheme->partsupfunc;
676 context.stepcmpfuncs = (FmgrInfo *) palloc0(sizeof(FmgrInfo) *
677 context.partnatts *
678 list_length(pruning_steps));
679 context.ppccontext = CurrentMemoryContext;
680
681 /* These are not valid when being called from the planner */
682 context.partrel = NULL;
683 context.planstate = NULL;
684 context.exprstates = NULL;
685
686 /* Actual pruning happens here. */
687 partindexes = get_matching_partitions(&context, pruning_steps);
688
689 /* Add selected partitions' RT indexes to result. */
690 i = -1;
691 result = NULL;
692 while ((i = bms_next_member(partindexes, i)) >= 0)
693 result = bms_add_member(result, rel->part_rels[i]->relid);
694
695 return result;
696 }
697
698 /*
699 * get_matching_partitions
700 * Determine partitions that survive partition pruning
701 *
702 * Note: context->planstate must be set to a valid PlanState when the
703 * pruning_steps were generated with a target other than PARTTARGET_PLANNER.
704 *
705 * Returns a Bitmapset of the RelOptInfo->part_rels indexes of the surviving
706 * partitions.
707 */
708 Bitmapset *
get_matching_partitions(PartitionPruneContext * context,List * pruning_steps)709 get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
710 {
711 Bitmapset *result;
712 int num_steps = list_length(pruning_steps),
713 i;
714 PruneStepResult **results,
715 *final_result;
716 ListCell *lc;
717 bool scan_default;
718
719 /* If there are no pruning steps then all partitions match. */
720 if (num_steps == 0)
721 {
722 Assert(context->nparts > 0);
723 return bms_add_range(NULL, 0, context->nparts - 1);
724 }
725
726 /*
727 * Allocate space for individual pruning steps to store its result. Each
728 * slot will hold a PruneStepResult after performing a given pruning step.
729 * Later steps may use the result of one or more earlier steps. The
730 * result of applying all pruning steps is the value contained in the slot
731 * of the last pruning step.
732 */
733 results = (PruneStepResult **)
734 palloc0(num_steps * sizeof(PruneStepResult *));
735 foreach(lc, pruning_steps)
736 {
737 PartitionPruneStep *step = lfirst(lc);
738
739 switch (nodeTag(step))
740 {
741 case T_PartitionPruneStepOp:
742 results[step->step_id] =
743 perform_pruning_base_step(context,
744 (PartitionPruneStepOp *) step);
745 break;
746
747 case T_PartitionPruneStepCombine:
748 results[step->step_id] =
749 perform_pruning_combine_step(context,
750 (PartitionPruneStepCombine *) step,
751 results);
752 break;
753
754 default:
755 elog(ERROR, "invalid pruning step type: %d",
756 (int) nodeTag(step));
757 }
758 }
759
760 /*
761 * At this point we know the offsets of all the datums whose corresponding
762 * partitions need to be in the result, including special null-accepting
763 * and default partitions. Collect the actual partition indexes now.
764 */
765 final_result = results[num_steps - 1];
766 Assert(final_result != NULL);
767 i = -1;
768 result = NULL;
769 scan_default = final_result->scan_default;
770 while ((i = bms_next_member(final_result->bound_offsets, i)) >= 0)
771 {
772 int partindex;
773
774 Assert(i < context->boundinfo->nindexes);
775 partindex = context->boundinfo->indexes[i];
776
777 if (partindex < 0)
778 {
779 /*
780 * In range partitioning cases, if a partition index is -1 it
781 * means that the bound at the offset is the upper bound for a
782 * range not covered by any partition (other than a possible
783 * default partition). In hash partitioning, the same means no
784 * partition has been defined for the corresponding remainder
785 * value.
786 *
787 * In either case, the value is still part of the queried range of
788 * values, so mark to scan the default partition if one exists.
789 */
790 scan_default |= partition_bound_has_default(context->boundinfo);
791 continue;
792 }
793
794 result = bms_add_member(result, partindex);
795 }
796
797 /* Add the null and/or default partition if needed and present. */
798 if (final_result->scan_null)
799 {
800 Assert(context->strategy == PARTITION_STRATEGY_LIST);
801 Assert(partition_bound_accepts_nulls(context->boundinfo));
802 result = bms_add_member(result, context->boundinfo->null_index);
803 }
804 if (scan_default)
805 {
806 Assert(context->strategy == PARTITION_STRATEGY_LIST ||
807 context->strategy == PARTITION_STRATEGY_RANGE);
808 Assert(partition_bound_has_default(context->boundinfo));
809 result = bms_add_member(result, context->boundinfo->default_index);
810 }
811
812 return result;
813 }
814
815 /*
816 * gen_partprune_steps_internal
817 * Processes 'clauses' to generate partition pruning steps.
818 *
819 * From OpExpr clauses that are mutually AND'd, we find combinations of those
820 * that match to the partition key columns and for every such combination,
821 * we emit a PartitionPruneStepOp containing a vector of expressions whose
822 * values are used as a look up key to search partitions by comparing the
823 * values with partition bounds. Relevant details of the operator and a
824 * vector of (possibly cross-type) comparison functions is also included with
825 * each step.
826 *
827 * For BoolExpr clauses, we recursively generate steps for each argument, and
828 * return a PartitionPruneStepCombine of their results.
829 *
830 * The return value is a list of the steps generated, which are also added to
831 * the context's steps list. Each step is assigned a step identifier, unique
832 * even across recursive calls.
833 *
834 * If we find clauses that are mutually contradictory, or a pseudoconstant
835 * clause that contains false, we set context->contradictory to true and
836 * return NIL (that is, no pruning steps). Caller should consider all
837 * partitions as pruned in that case.
838 */
839 static List *
gen_partprune_steps_internal(GeneratePruningStepsContext * context,List * clauses)840 gen_partprune_steps_internal(GeneratePruningStepsContext *context,
841 List *clauses)
842 {
843 PartitionScheme part_scheme = context->rel->part_scheme;
844 List *keyclauses[PARTITION_MAX_KEYS];
845 Bitmapset *nullkeys = NULL,
846 *notnullkeys = NULL;
847 bool generate_opsteps = false;
848 List *result = NIL;
849 ListCell *lc;
850
851 memset(keyclauses, 0, sizeof(keyclauses));
852 foreach(lc, clauses)
853 {
854 Expr *clause = (Expr *) lfirst(lc);
855 int i;
856
857 /* Look through RestrictInfo, if any */
858 if (IsA(clause, RestrictInfo))
859 clause = ((RestrictInfo *) clause)->clause;
860
861 /* Constant-false-or-null is contradictory */
862 if (IsA(clause, Const) &&
863 (((Const *) clause)->constisnull ||
864 !DatumGetBool(((Const *) clause)->constvalue)))
865 {
866 context->contradictory = true;
867 return NIL;
868 }
869
870 /* Get the BoolExpr's out of the way. */
871 if (IsA(clause, BoolExpr))
872 {
873 /*
874 * Generate steps for arguments.
875 *
876 * While steps generated for the arguments themselves will be
877 * added to context->steps during recursion and will be evaluated
878 * independently, collect their step IDs to be stored in the
879 * combine step we'll be creating.
880 */
881 if (or_clause((Node *) clause))
882 {
883 List *arg_stepids = NIL;
884 bool all_args_contradictory = true;
885 ListCell *lc1;
886
887 /*
888 * We can share the outer context area with the recursive
889 * call, but contradictory had better not be true yet.
890 */
891 Assert(!context->contradictory);
892
893 /*
894 * Get pruning step for each arg. If we get contradictory for
895 * all args, it means the OR expression is false as a whole.
896 */
897 foreach(lc1, ((BoolExpr *) clause)->args)
898 {
899 Expr *arg = lfirst(lc1);
900 bool arg_contradictory;
901 List *argsteps;
902
903 argsteps = gen_partprune_steps_internal(context,
904 list_make1(arg));
905 arg_contradictory = context->contradictory;
906 /* Keep context->contradictory clear till we're done */
907 context->contradictory = false;
908
909 if (arg_contradictory)
910 {
911 /* Just ignore self-contradictory arguments. */
912 continue;
913 }
914 else
915 all_args_contradictory = false;
916
917 if (argsteps != NIL)
918 {
919 PartitionPruneStep *step;
920
921 Assert(list_length(argsteps) == 1);
922 step = (PartitionPruneStep *) linitial(argsteps);
923 arg_stepids = lappend_int(arg_stepids, step->step_id);
924 }
925 else
926 {
927 /*
928 * The arg didn't contain a clause matching this
929 * partition key. We cannot prune using such an arg.
930 * To indicate that to the pruning code, we must
931 * construct a dummy PartitionPruneStepCombine whose
932 * source_stepids is set to an empty List.
933 *
934 * However, if we can prove using constraint exclusion
935 * that the clause refutes the table's partition
936 * constraint (if it's sub-partitioned), we need not
937 * bother with that. That is, we effectively ignore
938 * this OR arm.
939 */
940 List *partconstr = context->rel->partition_qual;
941 PartitionPruneStep *orstep;
942
943 if (partconstr)
944 {
945 partconstr = (List *)
946 expression_planner((Expr *) partconstr);
947 if (context->rel->relid != 1)
948 ChangeVarNodes((Node *) partconstr, 1,
949 context->rel->relid, 0);
950 if (predicate_refuted_by(partconstr,
951 list_make1(arg),
952 false))
953 continue;
954 }
955
956 orstep = gen_prune_step_combine(context, NIL,
957 PARTPRUNE_COMBINE_UNION);
958 arg_stepids = lappend_int(arg_stepids, orstep->step_id);
959 }
960 }
961
962 /* If all the OR arms are contradictory, we can stop */
963 if (all_args_contradictory)
964 {
965 context->contradictory = true;
966 return NIL;
967 }
968
969 if (arg_stepids != NIL)
970 {
971 PartitionPruneStep *step;
972
973 step = gen_prune_step_combine(context, arg_stepids,
974 PARTPRUNE_COMBINE_UNION);
975 result = lappend(result, step);
976 }
977 continue;
978 }
979 else if (and_clause((Node *) clause))
980 {
981 List *args = ((BoolExpr *) clause)->args;
982 List *argsteps,
983 *arg_stepids = NIL;
984 ListCell *lc1;
985
986 /*
987 * args may itself contain clauses of arbitrary type, so just
988 * recurse and later combine the component partitions sets
989 * using a combine step.
990 */
991 argsteps = gen_partprune_steps_internal(context, args);
992
993 /* If any AND arm is contradictory, we can stop immediately */
994 if (context->contradictory)
995 return NIL;
996
997 foreach(lc1, argsteps)
998 {
999 PartitionPruneStep *step = lfirst(lc1);
1000
1001 arg_stepids = lappend_int(arg_stepids, step->step_id);
1002 }
1003
1004 if (arg_stepids != NIL)
1005 {
1006 PartitionPruneStep *step;
1007
1008 step = gen_prune_step_combine(context, arg_stepids,
1009 PARTPRUNE_COMBINE_INTERSECT);
1010 result = lappend(result, step);
1011 }
1012 continue;
1013 }
1014
1015 /*
1016 * Fall-through for a NOT clause, which if it's a Boolean clause,
1017 * will be handled in match_clause_to_partition_key(). We
1018 * currently don't perform any pruning for more complex NOT
1019 * clauses.
1020 */
1021 }
1022
1023 /*
1024 * See if we can match this clause to any of the partition keys.
1025 */
1026 for (i = 0; i < part_scheme->partnatts; i++)
1027 {
1028 Expr *partkey = linitial(context->rel->partexprs[i]);
1029 bool clause_is_not_null = false;
1030 PartClauseInfo *pc = NULL;
1031 List *clause_steps = NIL;
1032
1033 switch (match_clause_to_partition_key(context,
1034 clause, partkey, i,
1035 &clause_is_not_null,
1036 &pc, &clause_steps))
1037 {
1038 case PARTCLAUSE_MATCH_CLAUSE:
1039 Assert(pc != NULL);
1040
1041 /*
1042 * Since we only allow strict operators, check for any
1043 * contradicting IS NULL.
1044 */
1045 if (bms_is_member(i, nullkeys))
1046 {
1047 context->contradictory = true;
1048 return NIL;
1049 }
1050 generate_opsteps = true;
1051 keyclauses[i] = lappend(keyclauses[i], pc);
1052 break;
1053
1054 case PARTCLAUSE_MATCH_NULLNESS:
1055 if (!clause_is_not_null)
1056 {
1057 /*
1058 * check for conflicting IS NOT NULL as well as
1059 * contradicting strict clauses
1060 */
1061 if (bms_is_member(i, notnullkeys) ||
1062 keyclauses[i] != NIL)
1063 {
1064 context->contradictory = true;
1065 return NIL;
1066 }
1067 nullkeys = bms_add_member(nullkeys, i);
1068 }
1069 else
1070 {
1071 /* check for conflicting IS NULL */
1072 if (bms_is_member(i, nullkeys))
1073 {
1074 context->contradictory = true;
1075 return NIL;
1076 }
1077 notnullkeys = bms_add_member(notnullkeys, i);
1078 }
1079 break;
1080
1081 case PARTCLAUSE_MATCH_STEPS:
1082 Assert(clause_steps != NIL);
1083 result = list_concat(result, clause_steps);
1084 break;
1085
1086 case PARTCLAUSE_MATCH_CONTRADICT:
1087 /* We've nothing more to do if a contradiction was found. */
1088 context->contradictory = true;
1089 return NIL;
1090
1091 case PARTCLAUSE_NOMATCH:
1092
1093 /*
1094 * Clause didn't match this key, but it might match the
1095 * next one.
1096 */
1097 continue;
1098
1099 case PARTCLAUSE_UNSUPPORTED:
1100 /* This clause cannot be used for pruning. */
1101 break;
1102 }
1103
1104 /* done; go check the next clause. */
1105 break;
1106 }
1107 }
1108
1109 /*-----------
1110 * Now generate some (more) pruning steps. We have three strategies:
1111 *
1112 * 1) Generate pruning steps based on IS NULL clauses:
1113 * a) For list partitioning, null partition keys can only be found in
1114 * the designated null-accepting partition, so if there are IS NULL
1115 * clauses containing partition keys we should generate a pruning
1116 * step that gets rid of all partitions but that one. We can
1117 * disregard any OpExpr we may have found.
1118 * b) For range partitioning, only the default partition can contain
1119 * NULL values, so the same rationale applies.
1120 * c) For hash partitioning, we only apply this strategy if we have
1121 * IS NULL clauses for all the keys. Strategy 2 below will take
1122 * care of the case where some keys have OpExprs and others have
1123 * IS NULL clauses.
1124 *
1125 * 2) If not, generate steps based on OpExprs we have (if any).
1126 *
1127 * 3) If this doesn't work either, we may be able to generate steps to
1128 * prune just the null-accepting partition (if one exists), if we have
1129 * IS NOT NULL clauses for all partition keys.
1130 */
1131 if (!bms_is_empty(nullkeys) &&
1132 (part_scheme->strategy == PARTITION_STRATEGY_LIST ||
1133 part_scheme->strategy == PARTITION_STRATEGY_RANGE ||
1134 (part_scheme->strategy == PARTITION_STRATEGY_HASH &&
1135 bms_num_members(nullkeys) == part_scheme->partnatts)))
1136 {
1137 PartitionPruneStep *step;
1138
1139 /* Strategy 1 */
1140 step = gen_prune_step_op(context, InvalidStrategy,
1141 false, NIL, NIL, nullkeys);
1142 result = lappend(result, step);
1143 }
1144 else if (generate_opsteps)
1145 {
1146 PartitionPruneStep *step;
1147
1148 /* Strategy 2 */
1149 step = gen_prune_steps_from_opexps(context, keyclauses, nullkeys);
1150 if (step != NULL)
1151 result = lappend(result, step);
1152 }
1153 else if (bms_num_members(notnullkeys) == part_scheme->partnatts)
1154 {
1155 PartitionPruneStep *step;
1156
1157 /* Strategy 3 */
1158 step = gen_prune_step_op(context, InvalidStrategy,
1159 false, NIL, NIL, NULL);
1160 result = lappend(result, step);
1161 }
1162
1163 /*
1164 * Finally, results from all entries appearing in result should be
1165 * combined using an INTERSECT combine step, if more than one.
1166 */
1167 if (list_length(result) > 1)
1168 {
1169 List *step_ids = NIL;
1170
1171 foreach(lc, result)
1172 {
1173 PartitionPruneStep *step = lfirst(lc);
1174
1175 step_ids = lappend_int(step_ids, step->step_id);
1176 }
1177
1178 if (step_ids != NIL)
1179 {
1180 PartitionPruneStep *step;
1181
1182 step = gen_prune_step_combine(context, step_ids,
1183 PARTPRUNE_COMBINE_INTERSECT);
1184 result = lappend(result, step);
1185 }
1186 }
1187
1188 return result;
1189 }
1190
1191 /*
1192 * gen_prune_step_op
1193 * Generate a pruning step for a specific operator
1194 *
1195 * The step is assigned a unique step identifier and added to context's 'steps'
1196 * list.
1197 */
1198 static PartitionPruneStep *
gen_prune_step_op(GeneratePruningStepsContext * context,StrategyNumber opstrategy,bool op_is_ne,List * exprs,List * cmpfns,Bitmapset * nullkeys)1199 gen_prune_step_op(GeneratePruningStepsContext *context,
1200 StrategyNumber opstrategy, bool op_is_ne,
1201 List *exprs, List *cmpfns,
1202 Bitmapset *nullkeys)
1203 {
1204 PartitionPruneStepOp *opstep = makeNode(PartitionPruneStepOp);
1205
1206 opstep->step.step_id = context->next_step_id++;
1207
1208 /*
1209 * For clauses that contain an <> operator, set opstrategy to
1210 * InvalidStrategy to signal get_matching_list_bounds to do the right
1211 * thing.
1212 */
1213 opstep->opstrategy = op_is_ne ? InvalidStrategy : opstrategy;
1214 Assert(list_length(exprs) == list_length(cmpfns));
1215 opstep->exprs = exprs;
1216 opstep->cmpfns = cmpfns;
1217 opstep->nullkeys = nullkeys;
1218
1219 context->steps = lappend(context->steps, opstep);
1220
1221 return (PartitionPruneStep *) opstep;
1222 }
1223
1224 /*
1225 * gen_prune_step_combine
1226 * Generate a pruning step for a combination of several other steps
1227 *
1228 * The step is assigned a unique step identifier and added to context's
1229 * 'steps' list.
1230 */
1231 static PartitionPruneStep *
gen_prune_step_combine(GeneratePruningStepsContext * context,List * source_stepids,PartitionPruneCombineOp combineOp)1232 gen_prune_step_combine(GeneratePruningStepsContext *context,
1233 List *source_stepids,
1234 PartitionPruneCombineOp combineOp)
1235 {
1236 PartitionPruneStepCombine *cstep = makeNode(PartitionPruneStepCombine);
1237
1238 cstep->step.step_id = context->next_step_id++;
1239 cstep->combineOp = combineOp;
1240 cstep->source_stepids = source_stepids;
1241
1242 context->steps = lappend(context->steps, cstep);
1243
1244 return (PartitionPruneStep *) cstep;
1245 }
1246
1247 /*
1248 * gen_prune_steps_from_opexps
1249 * Generate pruning steps based on clauses for partition keys
1250 *
1251 * 'keyclauses' contains one list of clauses per partition key. We check here
1252 * if we have found clauses for a valid subset of the partition key. In some
1253 * cases, (depending on the type of partitioning being used) if we didn't
1254 * find clauses for a given key, we discard clauses that may have been
1255 * found for any subsequent keys; see specific notes below.
1256 */
1257 static PartitionPruneStep *
gen_prune_steps_from_opexps(GeneratePruningStepsContext * context,List ** keyclauses,Bitmapset * nullkeys)1258 gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
1259 List **keyclauses, Bitmapset *nullkeys)
1260 {
1261 PartitionScheme part_scheme = context->rel->part_scheme;
1262 List *opsteps = NIL;
1263 List *btree_clauses[BTMaxStrategyNumber + 1],
1264 *hash_clauses[HTMaxStrategyNumber + 1];
1265 int i;
1266 ListCell *lc;
1267
1268 memset(btree_clauses, 0, sizeof(btree_clauses));
1269 memset(hash_clauses, 0, sizeof(hash_clauses));
1270 for (i = 0; i < part_scheme->partnatts; i++)
1271 {
1272 List *clauselist = keyclauses[i];
1273 bool consider_next_key = true;
1274
1275 /*
1276 * For range partitioning, if we have no clauses for the current key,
1277 * we can't consider any later keys either, so we can stop here.
1278 */
1279 if (part_scheme->strategy == PARTITION_STRATEGY_RANGE &&
1280 clauselist == NIL)
1281 break;
1282
1283 /*
1284 * For hash partitioning, if a column doesn't have the necessary
1285 * equality clause, there should be an IS NULL clause, otherwise
1286 * pruning is not possible.
1287 */
1288 if (part_scheme->strategy == PARTITION_STRATEGY_HASH &&
1289 clauselist == NIL && !bms_is_member(i, nullkeys))
1290 return NULL;
1291
1292 foreach(lc, clauselist)
1293 {
1294 PartClauseInfo *pc = (PartClauseInfo *) lfirst(lc);
1295 Oid lefttype,
1296 righttype;
1297
1298 /* Look up the operator's btree/hash strategy number. */
1299 if (pc->op_strategy == InvalidStrategy)
1300 get_op_opfamily_properties(pc->opno,
1301 part_scheme->partopfamily[i],
1302 false,
1303 &pc->op_strategy,
1304 &lefttype,
1305 &righttype);
1306
1307 switch (part_scheme->strategy)
1308 {
1309 case PARTITION_STRATEGY_LIST:
1310 case PARTITION_STRATEGY_RANGE:
1311 btree_clauses[pc->op_strategy] =
1312 lappend(btree_clauses[pc->op_strategy], pc);
1313
1314 /*
1315 * We can't consider subsequent partition keys if the
1316 * clause for the current key contains a non-inclusive
1317 * operator.
1318 */
1319 if (pc->op_strategy == BTLessStrategyNumber ||
1320 pc->op_strategy == BTGreaterStrategyNumber)
1321 consider_next_key = false;
1322 break;
1323
1324 case PARTITION_STRATEGY_HASH:
1325 if (pc->op_strategy != HTEqualStrategyNumber)
1326 elog(ERROR, "invalid clause for hash partitioning");
1327 hash_clauses[pc->op_strategy] =
1328 lappend(hash_clauses[pc->op_strategy], pc);
1329 break;
1330
1331 default:
1332 elog(ERROR, "invalid partition strategy: %c",
1333 part_scheme->strategy);
1334 break;
1335 }
1336 }
1337
1338 /*
1339 * If we've decided that clauses for subsequent partition keys
1340 * wouldn't be useful for pruning, don't search any further.
1341 */
1342 if (!consider_next_key)
1343 break;
1344 }
1345
1346 /*
1347 * Now, we have divided clauses according to their operator strategies.
1348 * Check for each strategy if we can generate pruning step(s) by
1349 * collecting a list of expressions whose values will constitute a vector
1350 * that can be used as a lookup key by a partition bound searching
1351 * function.
1352 */
1353 switch (part_scheme->strategy)
1354 {
1355 case PARTITION_STRATEGY_LIST:
1356 case PARTITION_STRATEGY_RANGE:
1357 {
1358 List *eq_clauses = btree_clauses[BTEqualStrategyNumber];
1359 List *le_clauses = btree_clauses[BTLessEqualStrategyNumber];
1360 List *ge_clauses = btree_clauses[BTGreaterEqualStrategyNumber];
1361 int strat;
1362
1363 /*
1364 * For each clause under consideration for a given strategy,
1365 * we collect expressions from clauses for earlier keys, whose
1366 * operator strategy is inclusive, into a list called
1367 * 'prefix'. By appending the clause's own expression to the
1368 * 'prefix', we'll generate one step using the so generated
1369 * vector and assign the current strategy to it. Actually,
1370 * 'prefix' might contain multiple clauses for the same key,
1371 * in which case, we must generate steps for various
1372 * combinations of expressions of different keys, which
1373 * get_steps_using_prefix takes care of for us.
1374 */
1375 for (strat = 1; strat <= BTMaxStrategyNumber; strat++)
1376 {
1377 foreach(lc, btree_clauses[strat])
1378 {
1379 PartClauseInfo *pc = lfirst(lc);
1380 ListCell *eq_start;
1381 ListCell *le_start;
1382 ListCell *ge_start;
1383 ListCell *lc1;
1384 List *prefix = NIL;
1385 List *pc_steps;
1386 bool prefix_valid = true;
1387 bool pk_has_clauses;
1388 int keyno;
1389
1390 /*
1391 * If this is a clause for the first partition key,
1392 * there are no preceding expressions; generate a
1393 * pruning step without a prefix.
1394 *
1395 * Note that we pass NULL for step_nullkeys, because
1396 * we don't search list/range partition bounds where
1397 * some keys are NULL.
1398 */
1399 if (pc->keyno == 0)
1400 {
1401 Assert(pc->op_strategy == strat);
1402 pc_steps = get_steps_using_prefix(context, strat,
1403 pc->op_is_ne,
1404 pc->expr,
1405 pc->cmpfn,
1406 0,
1407 NULL,
1408 NIL);
1409 opsteps = list_concat(opsteps, pc_steps);
1410 continue;
1411 }
1412
1413 eq_start = list_head(eq_clauses);
1414 le_start = list_head(le_clauses);
1415 ge_start = list_head(ge_clauses);
1416
1417 /*
1418 * We arrange clauses into prefix in ascending order
1419 * of their partition key numbers.
1420 */
1421 for (keyno = 0; keyno < pc->keyno; keyno++)
1422 {
1423 pk_has_clauses = false;
1424
1425 /*
1426 * Expressions from = clauses can always be in the
1427 * prefix, provided they're from an earlier key.
1428 */
1429 for_each_cell(lc1, eq_start)
1430 {
1431 PartClauseInfo *eqpc = lfirst(lc1);
1432
1433 if (eqpc->keyno == keyno)
1434 {
1435 prefix = lappend(prefix, eqpc);
1436 pk_has_clauses = true;
1437 }
1438 else
1439 {
1440 Assert(eqpc->keyno > keyno);
1441 break;
1442 }
1443 }
1444 eq_start = lc1;
1445
1446 /*
1447 * If we're generating steps for </<= strategy, we
1448 * can add other <= clauses to the prefix,
1449 * provided they're from an earlier key.
1450 */
1451 if (strat == BTLessStrategyNumber ||
1452 strat == BTLessEqualStrategyNumber)
1453 {
1454 for_each_cell(lc1, le_start)
1455 {
1456 PartClauseInfo *lepc = lfirst(lc1);
1457
1458 if (lepc->keyno == keyno)
1459 {
1460 prefix = lappend(prefix, lepc);
1461 pk_has_clauses = true;
1462 }
1463 else
1464 {
1465 Assert(lepc->keyno > keyno);
1466 break;
1467 }
1468 }
1469 le_start = lc1;
1470 }
1471
1472 /*
1473 * If we're generating steps for >/>= strategy, we
1474 * can add other >= clauses to the prefix,
1475 * provided they're from an earlier key.
1476 */
1477 if (strat == BTGreaterStrategyNumber ||
1478 strat == BTGreaterEqualStrategyNumber)
1479 {
1480 for_each_cell(lc1, ge_start)
1481 {
1482 PartClauseInfo *gepc = lfirst(lc1);
1483
1484 if (gepc->keyno == keyno)
1485 {
1486 prefix = lappend(prefix, gepc);
1487 pk_has_clauses = true;
1488 }
1489 else
1490 {
1491 Assert(gepc->keyno > keyno);
1492 break;
1493 }
1494 }
1495 ge_start = lc1;
1496 }
1497
1498 /*
1499 * If this key has no clauses, prefix is not valid
1500 * anymore.
1501 */
1502 if (!pk_has_clauses)
1503 {
1504 prefix_valid = false;
1505 break;
1506 }
1507 }
1508
1509 /*
1510 * If prefix_valid, generate PartitionPruneStepOps.
1511 * Otherwise, we would not find clauses for a valid
1512 * subset of the partition keys anymore for the
1513 * strategy; give up on generating partition pruning
1514 * steps further for the strategy.
1515 *
1516 * As mentioned above, if 'prefix' contains multiple
1517 * expressions for the same key, the following will
1518 * generate multiple steps, one for each combination
1519 * of the expressions for different keys.
1520 *
1521 * Note that we pass NULL for step_nullkeys, because
1522 * we don't search list/range partition bounds where
1523 * some keys are NULL.
1524 */
1525 if (prefix_valid)
1526 {
1527 Assert(pc->op_strategy == strat);
1528 pc_steps = get_steps_using_prefix(context, strat,
1529 pc->op_is_ne,
1530 pc->expr,
1531 pc->cmpfn,
1532 pc->keyno,
1533 NULL,
1534 prefix);
1535 opsteps = list_concat(opsteps, pc_steps);
1536 }
1537 else
1538 break;
1539 }
1540 }
1541 break;
1542 }
1543
1544 case PARTITION_STRATEGY_HASH:
1545 {
1546 List *eq_clauses = hash_clauses[HTEqualStrategyNumber];
1547
1548 /* For hash partitioning, we have just the = strategy. */
1549 if (eq_clauses != NIL)
1550 {
1551 PartClauseInfo *pc;
1552 List *pc_steps;
1553 List *prefix = NIL;
1554 int last_keyno;
1555 ListCell *lc1;
1556
1557 /*
1558 * Locate the clause for the greatest column. This may
1559 * not belong to the last partition key, but it is the
1560 * clause belonging to the last partition key we found a
1561 * clause for above.
1562 */
1563 pc = llast(eq_clauses);
1564
1565 /*
1566 * There might be multiple clauses which matched to that
1567 * partition key; find the first such clause. While at
1568 * it, add all the clauses before that one to 'prefix'.
1569 */
1570 last_keyno = pc->keyno;
1571 foreach(lc, eq_clauses)
1572 {
1573 pc = lfirst(lc);
1574 if (pc->keyno == last_keyno)
1575 break;
1576 prefix = lappend(prefix, pc);
1577 }
1578
1579 /*
1580 * For each clause for the "last" column, after appending
1581 * the clause's own expression to the 'prefix', we'll
1582 * generate one step using the so generated vector and
1583 * assign = as its strategy. Actually, 'prefix' might
1584 * contain multiple clauses for the same key, in which
1585 * case, we must generate steps for various combinations
1586 * of expressions of different keys, which
1587 * get_steps_using_prefix will take care of for us.
1588 */
1589 for_each_cell(lc1, lc)
1590 {
1591 pc = lfirst(lc1);
1592
1593 /*
1594 * Note that we pass nullkeys for step_nullkeys,
1595 * because we need to tell hash partition bound search
1596 * function which of the keys we found IS NULL clauses
1597 * for.
1598 */
1599 Assert(pc->op_strategy == HTEqualStrategyNumber);
1600 pc_steps =
1601 get_steps_using_prefix(context,
1602 HTEqualStrategyNumber,
1603 false,
1604 pc->expr,
1605 pc->cmpfn,
1606 pc->keyno,
1607 nullkeys,
1608 prefix);
1609 opsteps = list_concat(opsteps, list_copy(pc_steps));
1610 }
1611 }
1612 break;
1613 }
1614
1615 default:
1616 elog(ERROR, "invalid partition strategy: %c",
1617 part_scheme->strategy);
1618 break;
1619 }
1620
1621 /* Lastly, add a combine step to mutually AND these op steps, if needed */
1622 if (list_length(opsteps) > 1)
1623 {
1624 List *opstep_ids = NIL;
1625
1626 foreach(lc, opsteps)
1627 {
1628 PartitionPruneStep *step = lfirst(lc);
1629
1630 opstep_ids = lappend_int(opstep_ids, step->step_id);
1631 }
1632
1633 if (opstep_ids != NIL)
1634 return gen_prune_step_combine(context, opstep_ids,
1635 PARTPRUNE_COMBINE_INTERSECT);
1636 return NULL;
1637 }
1638 else if (opsteps != NIL)
1639 return linitial(opsteps);
1640
1641 return NULL;
1642 }
1643
1644 /*
1645 * If the partition key has a collation, then the clause must have the same
1646 * input collation. If the partition key is non-collatable, we assume the
1647 * collation doesn't matter, because while collation wasn't considered when
1648 * performing partitioning, the clause still may have a collation assigned
1649 * due to the other input being of a collatable type.
1650 *
1651 * See also IndexCollMatchesExprColl.
1652 */
1653 #define PartCollMatchesExprColl(partcoll, exprcoll) \
1654 ((partcoll) == InvalidOid || (partcoll) == (exprcoll))
1655
1656 /*
1657 * match_clause_to_partition_key
1658 * Attempt to match the given 'clause' with the specified partition key.
1659 *
1660 * Return value is:
1661 * * PARTCLAUSE_NOMATCH if the clause doesn't match this partition key (but
1662 * caller should keep trying, because it might match a subsequent key).
1663 * Output arguments: none set.
1664 *
1665 * * PARTCLAUSE_MATCH_CLAUSE if there is a match.
1666 * Output arguments: *pc is set to a PartClauseInfo constructed for the
1667 * matched clause.
1668 *
1669 * * PARTCLAUSE_MATCH_NULLNESS if there is a match, and the matched clause was
1670 * either a "a IS NULL" or "a IS NOT NULL" clause.
1671 * Output arguments: *clause_is_not_null is set to false in the former case
1672 * true otherwise.
1673 *
1674 * * PARTCLAUSE_MATCH_STEPS if there is a match.
1675 * Output arguments: *clause_steps is set to a list of PartitionPruneStep
1676 * generated for the clause.
1677 *
1678 * * PARTCLAUSE_MATCH_CONTRADICT if the clause is self-contradictory, ie
1679 * it provably returns FALSE or NULL.
1680 * Output arguments: none set.
1681 *
1682 * * PARTCLAUSE_UNSUPPORTED if the clause doesn't match this partition key
1683 * and couldn't possibly match any other one either, due to its form or
1684 * properties (such as containing a volatile function).
1685 * Output arguments: none set.
1686 */
1687 static PartClauseMatchStatus
match_clause_to_partition_key(GeneratePruningStepsContext * context,Expr * clause,Expr * partkey,int partkeyidx,bool * clause_is_not_null,PartClauseInfo ** pc,List ** clause_steps)1688 match_clause_to_partition_key(GeneratePruningStepsContext *context,
1689 Expr *clause, Expr *partkey, int partkeyidx,
1690 bool *clause_is_not_null, PartClauseInfo **pc,
1691 List **clause_steps)
1692 {
1693 PartClauseMatchStatus boolmatchstatus;
1694 PartitionScheme part_scheme = context->rel->part_scheme;
1695 Oid partopfamily = part_scheme->partopfamily[partkeyidx],
1696 partcoll = part_scheme->partcollation[partkeyidx];
1697 Expr *expr;
1698
1699 /*
1700 * Recognize specially shaped clauses that match a Boolean partition key.
1701 */
1702 boolmatchstatus = match_boolean_partition_clause(partopfamily, clause,
1703 partkey, &expr);
1704
1705 if (boolmatchstatus == PARTCLAUSE_MATCH_CLAUSE)
1706 {
1707 PartClauseInfo *partclause;
1708
1709 partclause = (PartClauseInfo *) palloc(sizeof(PartClauseInfo));
1710 partclause->keyno = partkeyidx;
1711 /* Do pruning with the Boolean equality operator. */
1712 partclause->opno = BooleanEqualOperator;
1713 partclause->op_is_ne = false;
1714 partclause->expr = expr;
1715 /* We know that expr is of Boolean type. */
1716 partclause->cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid;
1717 partclause->op_strategy = InvalidStrategy;
1718
1719 *pc = partclause;
1720
1721 return PARTCLAUSE_MATCH_CLAUSE;
1722 }
1723 else if (IsA(clause, OpExpr) &&
1724 list_length(((OpExpr *) clause)->args) == 2)
1725 {
1726 OpExpr *opclause = (OpExpr *) clause;
1727 Expr *leftop,
1728 *rightop;
1729 Oid opno,
1730 op_lefttype,
1731 op_righttype,
1732 negator = InvalidOid;
1733 Oid cmpfn;
1734 int op_strategy;
1735 bool is_opne_listp = false;
1736 PartClauseInfo *partclause;
1737
1738 leftop = (Expr *) get_leftop(clause);
1739 if (IsA(leftop, RelabelType))
1740 leftop = ((RelabelType *) leftop)->arg;
1741 rightop = (Expr *) get_rightop(clause);
1742 if (IsA(rightop, RelabelType))
1743 rightop = ((RelabelType *) rightop)->arg;
1744 opno = opclause->opno;
1745
1746 /* check if the clause matches this partition key */
1747 if (equal(leftop, partkey))
1748 expr = rightop;
1749 else if (equal(rightop, partkey))
1750 {
1751 /*
1752 * It's only useful if we can commute the operator to put the
1753 * partkey on the left. If we can't, the clause can be deemed
1754 * UNSUPPORTED. Even if its leftop matches some later partkey, we
1755 * now know it has Vars on the right, so it's no use.
1756 */
1757 opno = get_commutator(opno);
1758 if (!OidIsValid(opno))
1759 return PARTCLAUSE_UNSUPPORTED;
1760 expr = leftop;
1761 }
1762 else
1763 /* clause does not match this partition key, but perhaps next. */
1764 return PARTCLAUSE_NOMATCH;
1765
1766 /*
1767 * Partition key match also requires collation match. There may be
1768 * multiple partkeys with the same expression but different
1769 * collations, so failure is NOMATCH.
1770 */
1771 if (!PartCollMatchesExprColl(partcoll, opclause->inputcollid))
1772 return PARTCLAUSE_NOMATCH;
1773
1774 /*
1775 * See if the operator is relevant to the partitioning opfamily.
1776 *
1777 * Normally we only care about operators that are listed as being part
1778 * of the partitioning operator family. But there is one exception:
1779 * the not-equals operators are not listed in any operator family
1780 * whatsoever, but their negators (equality) are. We can use one of
1781 * those if we find it, but only for list partitioning.
1782 *
1783 * Note: we report NOMATCH on failure, in case a later partkey has the
1784 * same expression but different opfamily. That's unlikely, but not
1785 * much more so than duplicate expressions with different collations.
1786 */
1787 if (op_in_opfamily(opno, partopfamily))
1788 {
1789 get_op_opfamily_properties(opno, partopfamily, false,
1790 &op_strategy, &op_lefttype,
1791 &op_righttype);
1792 }
1793 else
1794 {
1795 if (part_scheme->strategy != PARTITION_STRATEGY_LIST)
1796 return PARTCLAUSE_NOMATCH;
1797
1798 /* See if the negator is equality */
1799 negator = get_negator(opno);
1800 if (OidIsValid(negator) && op_in_opfamily(negator, partopfamily))
1801 {
1802 get_op_opfamily_properties(negator, partopfamily, false,
1803 &op_strategy, &op_lefttype,
1804 &op_righttype);
1805 if (op_strategy == BTEqualStrategyNumber)
1806 is_opne_listp = true; /* bingo */
1807 }
1808
1809 /* Nope, it's not <> either. */
1810 if (!is_opne_listp)
1811 return PARTCLAUSE_NOMATCH;
1812 }
1813
1814 /*
1815 * Only allow strict operators. This will guarantee nulls are
1816 * filtered. (This test is likely useless, since btree and hash
1817 * comparison operators are generally strict.)
1818 */
1819 if (!op_strict(opno))
1820 return PARTCLAUSE_UNSUPPORTED;
1821
1822 /*
1823 * OK, we have a match to the partition key and a suitable operator.
1824 * Examine the other argument to see if it's usable for pruning.
1825 *
1826 * In most of these cases, we can return UNSUPPORTED because the same
1827 * failure would occur no matter which partkey it's matched to. (In
1828 * particular, now that we've successfully matched one side of the
1829 * opclause to a partkey, there is no chance that matching the other
1830 * side to another partkey will produce a usable result, since that'd
1831 * mean there are Vars on both sides.)
1832 *
1833 * Also, if we reject an argument for a target-dependent reason, set
1834 * appropriate fields of *context to report that. We postpone these
1835 * tests until after matching the partkey and the operator, so as to
1836 * reduce the odds of setting the context fields for clauses that do
1837 * not end up contributing to pruning steps.
1838 *
1839 * First, check for non-Const argument. (We assume that any immutable
1840 * subexpression will have been folded to a Const already.)
1841 */
1842 if (!IsA(expr, Const))
1843 {
1844 Bitmapset *paramids;
1845
1846 /*
1847 * When pruning in the planner, we only support pruning using
1848 * comparisons to constants. We cannot prune on the basis of
1849 * anything that's not immutable. (Note that has_mutable_arg and
1850 * has_exec_param do not get set for this target value.)
1851 */
1852 if (context->target == PARTTARGET_PLANNER)
1853 return PARTCLAUSE_UNSUPPORTED;
1854
1855 /*
1856 * We can never prune using an expression that contains Vars.
1857 */
1858 if (contain_var_clause((Node *) expr))
1859 return PARTCLAUSE_UNSUPPORTED;
1860
1861 /*
1862 * And we must reject anything containing a volatile function.
1863 * Stable functions are OK though.
1864 */
1865 if (contain_volatile_functions((Node *) expr))
1866 return PARTCLAUSE_UNSUPPORTED;
1867
1868 /*
1869 * See if there are any exec Params. If so, we can only use this
1870 * expression during per-scan pruning.
1871 */
1872 paramids = pull_exec_paramids(expr);
1873 if (!bms_is_empty(paramids))
1874 {
1875 context->has_exec_param = true;
1876 if (context->target != PARTTARGET_EXEC)
1877 return PARTCLAUSE_UNSUPPORTED;
1878 }
1879 else
1880 {
1881 /* It's potentially usable, but mutable */
1882 context->has_mutable_arg = true;
1883 }
1884 }
1885
1886 /*
1887 * Check whether the comparison operator itself is immutable. (We
1888 * assume anything that's in a btree or hash opclass is at least
1889 * stable, but we need to check for immutability.)
1890 */
1891 if (op_volatile(opno) != PROVOLATILE_IMMUTABLE)
1892 {
1893 context->has_mutable_op = true;
1894
1895 /*
1896 * When pruning in the planner, we cannot prune with mutable
1897 * operators.
1898 */
1899 if (context->target == PARTTARGET_PLANNER)
1900 return PARTCLAUSE_UNSUPPORTED;
1901 }
1902
1903 /*
1904 * Now find the procedure to use, based on the types. If the clause's
1905 * other argument is of the same type as the partitioning opclass's
1906 * declared input type, we can use the procedure cached in
1907 * PartitionKey. If not, search for a cross-type one in the same
1908 * opfamily; if one doesn't exist, report no match.
1909 */
1910 if (op_righttype == part_scheme->partopcintype[partkeyidx])
1911 cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid;
1912 else
1913 {
1914 switch (part_scheme->strategy)
1915 {
1916 /*
1917 * For range and list partitioning, we need the ordering
1918 * procedure with lefttype being the partition key's type,
1919 * and righttype the clause's operator's right type.
1920 */
1921 case PARTITION_STRATEGY_LIST:
1922 case PARTITION_STRATEGY_RANGE:
1923 cmpfn =
1924 get_opfamily_proc(part_scheme->partopfamily[partkeyidx],
1925 part_scheme->partopcintype[partkeyidx],
1926 op_righttype, BTORDER_PROC);
1927 break;
1928
1929 /*
1930 * For hash partitioning, we need the hashing procedure
1931 * for the clause's type.
1932 */
1933 case PARTITION_STRATEGY_HASH:
1934 cmpfn =
1935 get_opfamily_proc(part_scheme->partopfamily[partkeyidx],
1936 op_righttype, op_righttype,
1937 HASHEXTENDED_PROC);
1938 break;
1939
1940 default:
1941 elog(ERROR, "invalid partition strategy: %c",
1942 part_scheme->strategy);
1943 cmpfn = InvalidOid; /* keep compiler quiet */
1944 break;
1945 }
1946
1947 if (!OidIsValid(cmpfn))
1948 return PARTCLAUSE_NOMATCH;
1949 }
1950
1951 /*
1952 * Build the clause, passing the negator if applicable.
1953 */
1954 partclause = (PartClauseInfo *) palloc(sizeof(PartClauseInfo));
1955 partclause->keyno = partkeyidx;
1956 if (is_opne_listp)
1957 {
1958 Assert(OidIsValid(negator));
1959 partclause->opno = negator;
1960 partclause->op_is_ne = true;
1961 partclause->op_strategy = InvalidStrategy;
1962 }
1963 else
1964 {
1965 partclause->opno = opno;
1966 partclause->op_is_ne = false;
1967 partclause->op_strategy = op_strategy;
1968 }
1969 partclause->expr = expr;
1970 partclause->cmpfn = cmpfn;
1971
1972 *pc = partclause;
1973
1974 return PARTCLAUSE_MATCH_CLAUSE;
1975 }
1976 else if (IsA(clause, ScalarArrayOpExpr))
1977 {
1978 ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
1979 Oid saop_op = saop->opno;
1980 Oid saop_coll = saop->inputcollid;
1981 Expr *leftop = (Expr *) linitial(saop->args),
1982 *rightop = (Expr *) lsecond(saop->args);
1983 List *elem_exprs,
1984 *elem_clauses;
1985 ListCell *lc1;
1986
1987 if (IsA(leftop, RelabelType))
1988 leftop = ((RelabelType *) leftop)->arg;
1989
1990 /* check if the LHS matches this partition key */
1991 if (!equal(leftop, partkey) ||
1992 !PartCollMatchesExprColl(partcoll, saop->inputcollid))
1993 return PARTCLAUSE_NOMATCH;
1994
1995 /*
1996 * See if the operator is relevant to the partitioning opfamily.
1997 *
1998 * In case of NOT IN (..), we get a '<>', which we handle if list
1999 * partitioning is in use and we're able to confirm that it's negator
2000 * is a btree equality operator belonging to the partitioning operator
2001 * family. As above, report NOMATCH for non-matching operator.
2002 */
2003 if (!op_in_opfamily(saop_op, partopfamily))
2004 {
2005 Oid negator;
2006
2007 if (part_scheme->strategy != PARTITION_STRATEGY_LIST)
2008 return PARTCLAUSE_NOMATCH;
2009
2010 negator = get_negator(saop_op);
2011 if (OidIsValid(negator) && op_in_opfamily(negator, partopfamily))
2012 {
2013 int strategy;
2014 Oid lefttype,
2015 righttype;
2016
2017 get_op_opfamily_properties(negator, partopfamily,
2018 false, &strategy,
2019 &lefttype, &righttype);
2020 if (strategy != BTEqualStrategyNumber)
2021 return PARTCLAUSE_NOMATCH;
2022 }
2023 else
2024 return PARTCLAUSE_NOMATCH; /* no useful negator */
2025 }
2026
2027 /*
2028 * Only allow strict operators. This will guarantee nulls are
2029 * filtered. (This test is likely useless, since btree and hash
2030 * comparison operators are generally strict.)
2031 */
2032 if (!op_strict(saop_op))
2033 return PARTCLAUSE_UNSUPPORTED;
2034
2035 /*
2036 * OK, we have a match to the partition key and a suitable operator.
2037 * Examine the array argument to see if it's usable for pruning. This
2038 * is identical to the logic for a plain OpExpr.
2039 */
2040 if (!IsA(rightop, Const))
2041 {
2042 Bitmapset *paramids;
2043
2044 /*
2045 * When pruning in the planner, we only support pruning using
2046 * comparisons to constants. We cannot prune on the basis of
2047 * anything that's not immutable. (Note that has_mutable_arg and
2048 * has_exec_param do not get set for this target value.)
2049 */
2050 if (context->target == PARTTARGET_PLANNER)
2051 return PARTCLAUSE_UNSUPPORTED;
2052
2053 /*
2054 * We can never prune using an expression that contains Vars.
2055 */
2056 if (contain_var_clause((Node *) rightop))
2057 return PARTCLAUSE_UNSUPPORTED;
2058
2059 /*
2060 * And we must reject anything containing a volatile function.
2061 * Stable functions are OK though.
2062 */
2063 if (contain_volatile_functions((Node *) rightop))
2064 return PARTCLAUSE_UNSUPPORTED;
2065
2066 /*
2067 * See if there are any exec Params. If so, we can only use this
2068 * expression during per-scan pruning.
2069 */
2070 paramids = pull_exec_paramids(rightop);
2071 if (!bms_is_empty(paramids))
2072 {
2073 context->has_exec_param = true;
2074 if (context->target != PARTTARGET_EXEC)
2075 return PARTCLAUSE_UNSUPPORTED;
2076 }
2077 else
2078 {
2079 /* It's potentially usable, but mutable */
2080 context->has_mutable_arg = true;
2081 }
2082 }
2083
2084 /*
2085 * Check whether the comparison operator itself is immutable. (We
2086 * assume anything that's in a btree or hash opclass is at least
2087 * stable, but we need to check for immutability.)
2088 */
2089 if (op_volatile(saop_op) != PROVOLATILE_IMMUTABLE)
2090 {
2091 context->has_mutable_op = true;
2092
2093 /*
2094 * When pruning in the planner, we cannot prune with mutable
2095 * operators.
2096 */
2097 if (context->target == PARTTARGET_PLANNER)
2098 return PARTCLAUSE_UNSUPPORTED;
2099 }
2100
2101 /*
2102 * Examine the contents of the array argument.
2103 */
2104 elem_exprs = NIL;
2105 if (IsA(rightop, Const))
2106 {
2107 /*
2108 * For a constant array, convert the elements to a list of Const
2109 * nodes, one for each array element (excepting nulls).
2110 */
2111 Const *arr = (Const *) rightop;
2112 ArrayType *arrval;
2113 int16 elemlen;
2114 bool elembyval;
2115 char elemalign;
2116 Datum *elem_values;
2117 bool *elem_nulls;
2118 int num_elems,
2119 i;
2120
2121 /* If the array itself is null, the saop returns null */
2122 if (arr->constisnull)
2123 return PARTCLAUSE_MATCH_CONTRADICT;
2124
2125 arrval = DatumGetArrayTypeP(arr->constvalue);
2126 get_typlenbyvalalign(ARR_ELEMTYPE(arrval),
2127 &elemlen, &elembyval, &elemalign);
2128 deconstruct_array(arrval,
2129 ARR_ELEMTYPE(arrval),
2130 elemlen, elembyval, elemalign,
2131 &elem_values, &elem_nulls,
2132 &num_elems);
2133 for (i = 0; i < num_elems; i++)
2134 {
2135 Const *elem_expr;
2136
2137 /*
2138 * A null array element must lead to a null comparison result,
2139 * since saop_op is known strict. We can ignore it in the
2140 * useOr case, but otherwise it implies self-contradiction.
2141 */
2142 if (elem_nulls[i])
2143 {
2144 if (saop->useOr)
2145 continue;
2146 return PARTCLAUSE_MATCH_CONTRADICT;
2147 }
2148
2149 elem_expr = makeConst(ARR_ELEMTYPE(arrval), -1,
2150 arr->constcollid, elemlen,
2151 elem_values[i], false, elembyval);
2152 elem_exprs = lappend(elem_exprs, elem_expr);
2153 }
2154 }
2155 else if (IsA(rightop, ArrayExpr))
2156 {
2157 ArrayExpr *arrexpr = castNode(ArrayExpr, rightop);
2158
2159 /*
2160 * For a nested ArrayExpr, we don't know how to get the actual
2161 * scalar values out into a flat list, so we give up doing
2162 * anything with this ScalarArrayOpExpr.
2163 */
2164 if (arrexpr->multidims)
2165 return PARTCLAUSE_UNSUPPORTED;
2166
2167 /*
2168 * Otherwise, we can just use the list of element values.
2169 */
2170 elem_exprs = arrexpr->elements;
2171 }
2172 else
2173 {
2174 /* Give up on any other clause types. */
2175 return PARTCLAUSE_UNSUPPORTED;
2176 }
2177
2178 /*
2179 * Now generate a list of clauses, one for each array element, of the
2180 * form saop_leftop saop_op elem_expr
2181 */
2182 elem_clauses = NIL;
2183 foreach(lc1, elem_exprs)
2184 {
2185 Expr *rightop = (Expr *) lfirst(lc1),
2186 *elem_clause;
2187
2188 elem_clause = make_opclause(saop_op, BOOLOID, false,
2189 leftop, rightop,
2190 InvalidOid, saop_coll);
2191 elem_clauses = lappend(elem_clauses, elem_clause);
2192 }
2193
2194 /*
2195 * If we have an ANY clause and multiple elements, now turn the list
2196 * of clauses into an OR expression.
2197 */
2198 if (saop->useOr && list_length(elem_clauses) > 1)
2199 elem_clauses = list_make1(makeBoolExpr(OR_EXPR, elem_clauses, -1));
2200
2201 /* Finally, generate steps */
2202 *clause_steps = gen_partprune_steps_internal(context, elem_clauses);
2203 if (context->contradictory)
2204 return PARTCLAUSE_MATCH_CONTRADICT;
2205 else if (*clause_steps == NIL)
2206 return PARTCLAUSE_UNSUPPORTED; /* step generation failed */
2207 return PARTCLAUSE_MATCH_STEPS;
2208 }
2209 else if (IsA(clause, NullTest))
2210 {
2211 NullTest *nulltest = (NullTest *) clause;
2212 Expr *arg = nulltest->arg;
2213
2214 if (IsA(arg, RelabelType))
2215 arg = ((RelabelType *) arg)->arg;
2216
2217 /* Does arg match with this partition key column? */
2218 if (!equal(arg, partkey))
2219 return PARTCLAUSE_NOMATCH;
2220
2221 *clause_is_not_null = (nulltest->nulltesttype == IS_NOT_NULL);
2222
2223 return PARTCLAUSE_MATCH_NULLNESS;
2224 }
2225
2226 /*
2227 * If we get here then the return value depends on the result of the
2228 * match_boolean_partition_clause call above. If the call returned
2229 * PARTCLAUSE_UNSUPPORTED then we're either not dealing with a bool qual
2230 * or the bool qual is not suitable for pruning. Since the qual didn't
2231 * match up to any of the other qual types supported here, then trying to
2232 * match it against any other partition key is a waste of time, so just
2233 * return PARTCLAUSE_UNSUPPORTED. If the qual just couldn't be matched to
2234 * this partition key, then it may match another, so return
2235 * PARTCLAUSE_NOMATCH. The only other value that
2236 * match_boolean_partition_clause can return is PARTCLAUSE_MATCH_CLAUSE,
2237 * and since that value was already dealt with above, then we can just
2238 * return boolmatchstatus.
2239 */
2240 return boolmatchstatus;
2241 }
2242
2243 /*
2244 * get_steps_using_prefix
2245 * Generate list of PartitionPruneStepOp steps each consisting of given
2246 * opstrategy
2247 *
2248 * To generate steps, step_lastexpr and step_lastcmpfn are appended to
2249 * expressions and cmpfns, respectively, extracted from the clauses in
2250 * 'prefix'. Actually, since 'prefix' may contain multiple clauses for the
2251 * same partition key column, we must generate steps for various combinations
2252 * of the clauses of different keys.
2253 *
2254 * For list/range partitioning, callers must ensure that step_nullkeys is
2255 * NULL, and that prefix contains at least one clause for each of the
2256 * partition keys earlier than one specified in step_lastkeyno if it's
2257 * greater than zero. For hash partitioning, step_nullkeys is allowed to be
2258 * non-NULL, but they must ensure that prefix contains at least one clause
2259 * for each of the partition keys other than those specified in step_nullkeys
2260 * and step_lastkeyno.
2261 *
2262 * For both cases, callers must also ensure that clauses in prefix are sorted
2263 * in ascending order of their partition key numbers.
2264 */
2265 static List *
get_steps_using_prefix(GeneratePruningStepsContext * context,StrategyNumber step_opstrategy,bool step_op_is_ne,Expr * step_lastexpr,Oid step_lastcmpfn,int step_lastkeyno,Bitmapset * step_nullkeys,List * prefix)2266 get_steps_using_prefix(GeneratePruningStepsContext *context,
2267 StrategyNumber step_opstrategy,
2268 bool step_op_is_ne,
2269 Expr *step_lastexpr,
2270 Oid step_lastcmpfn,
2271 int step_lastkeyno,
2272 Bitmapset *step_nullkeys,
2273 List *prefix)
2274 {
2275 Assert(step_nullkeys == NULL ||
2276 context->rel->part_scheme->strategy == PARTITION_STRATEGY_HASH);
2277
2278 /* Quick exit if there are no values to prefix with. */
2279 if (list_length(prefix) == 0)
2280 {
2281 PartitionPruneStep *step;
2282
2283 step = gen_prune_step_op(context,
2284 step_opstrategy,
2285 step_op_is_ne,
2286 list_make1(step_lastexpr),
2287 list_make1_oid(step_lastcmpfn),
2288 step_nullkeys);
2289 return list_make1(step);
2290 }
2291
2292 /* Recurse to generate steps for various combinations. */
2293 return get_steps_using_prefix_recurse(context,
2294 step_opstrategy,
2295 step_op_is_ne,
2296 step_lastexpr,
2297 step_lastcmpfn,
2298 step_lastkeyno,
2299 step_nullkeys,
2300 list_head(prefix),
2301 NIL, NIL);
2302 }
2303
2304 /*
2305 * get_steps_using_prefix_recurse
2306 * Recursively generate combinations of clauses for different partition
2307 * keys and start generating steps upon reaching clauses for the greatest
2308 * column that is less than the one for which we're currently generating
2309 * steps (that is, step_lastkeyno)
2310 *
2311 * 'start' is where we should start iterating for the current invocation.
2312 * 'step_exprs' and 'step_cmpfns' each contains the expressions and cmpfns
2313 * we've generated so far from the clauses for the previous part keys.
2314 */
2315 static List *
get_steps_using_prefix_recurse(GeneratePruningStepsContext * context,StrategyNumber step_opstrategy,bool step_op_is_ne,Expr * step_lastexpr,Oid step_lastcmpfn,int step_lastkeyno,Bitmapset * step_nullkeys,ListCell * start,List * step_exprs,List * step_cmpfns)2316 get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
2317 StrategyNumber step_opstrategy,
2318 bool step_op_is_ne,
2319 Expr *step_lastexpr,
2320 Oid step_lastcmpfn,
2321 int step_lastkeyno,
2322 Bitmapset *step_nullkeys,
2323 ListCell *start,
2324 List *step_exprs,
2325 List *step_cmpfns)
2326 {
2327 List *result = NIL;
2328 ListCell *lc;
2329 int cur_keyno;
2330
2331 /* Actually, recursion would be limited by PARTITION_MAX_KEYS. */
2332 check_stack_depth();
2333
2334 /* Check if we need to recurse. */
2335 Assert(start != NULL);
2336 cur_keyno = ((PartClauseInfo *) lfirst(start))->keyno;
2337 if (cur_keyno < step_lastkeyno - 1)
2338 {
2339 PartClauseInfo *pc;
2340 ListCell *next_start;
2341
2342 /*
2343 * For each clause with cur_keyno, add its expr and cmpfn to
2344 * step_exprs and step_cmpfns, respectively, and recurse after setting
2345 * next_start to the ListCell of the first clause for the next
2346 * partition key.
2347 */
2348 for_each_cell(lc, start)
2349 {
2350 pc = lfirst(lc);
2351
2352 if (pc->keyno > cur_keyno)
2353 break;
2354 }
2355 next_start = lc;
2356
2357 for_each_cell(lc, start)
2358 {
2359 List *moresteps;
2360 List *step_exprs1,
2361 *step_cmpfns1;
2362
2363 pc = lfirst(lc);
2364 if (pc->keyno == cur_keyno)
2365 {
2366 /* Leave the original step_exprs unmodified. */
2367 step_exprs1 = list_copy(step_exprs);
2368 step_exprs1 = lappend(step_exprs1, pc->expr);
2369
2370 /* Leave the original step_cmpfns unmodified. */
2371 step_cmpfns1 = list_copy(step_cmpfns);
2372 step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2373 }
2374 else
2375 {
2376 Assert(pc->keyno > cur_keyno);
2377 break;
2378 }
2379
2380 moresteps = get_steps_using_prefix_recurse(context,
2381 step_opstrategy,
2382 step_op_is_ne,
2383 step_lastexpr,
2384 step_lastcmpfn,
2385 step_lastkeyno,
2386 step_nullkeys,
2387 next_start,
2388 step_exprs1,
2389 step_cmpfns1);
2390 result = list_concat(result, moresteps);
2391
2392 list_free(step_exprs1);
2393 list_free(step_cmpfns1);
2394 }
2395 }
2396 else
2397 {
2398 /*
2399 * End the current recursion cycle and start generating steps, one for
2400 * each clause with cur_keyno, which is all clauses from here onward
2401 * till the end of the list. Note that for hash partitioning,
2402 * step_nullkeys is allowed to be non-empty, in which case step_exprs
2403 * would only contain expressions for the earlier partition keys that
2404 * are not specified in step_nullkeys.
2405 */
2406 Assert(list_length(step_exprs) == cur_keyno ||
2407 !bms_is_empty(step_nullkeys));
2408 /*
2409 * Note also that for hash partitioning, each partition key should
2410 * have either equality clauses or an IS NULL clause, so if a
2411 * partition key doesn't have an expression, it would be specified
2412 * in step_nullkeys.
2413 */
2414 Assert(context->rel->part_scheme->strategy
2415 != PARTITION_STRATEGY_HASH ||
2416 list_length(step_exprs) + 2 + bms_num_members(step_nullkeys) ==
2417 context->rel->part_scheme->partnatts);
2418 for_each_cell(lc, start)
2419 {
2420 PartClauseInfo *pc = lfirst(lc);
2421 PartitionPruneStep *step;
2422 List *step_exprs1,
2423 *step_cmpfns1;
2424
2425 Assert(pc->keyno == cur_keyno);
2426
2427 /* Leave the original step_exprs unmodified. */
2428 step_exprs1 = list_copy(step_exprs);
2429 step_exprs1 = lappend(step_exprs1, pc->expr);
2430 step_exprs1 = lappend(step_exprs1, step_lastexpr);
2431
2432 /* Leave the original step_cmpfns unmodified. */
2433 step_cmpfns1 = list_copy(step_cmpfns);
2434 step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2435 step_cmpfns1 = lappend_oid(step_cmpfns1, step_lastcmpfn);
2436
2437 step = gen_prune_step_op(context,
2438 step_opstrategy, step_op_is_ne,
2439 step_exprs1, step_cmpfns1,
2440 step_nullkeys);
2441 result = lappend(result, step);
2442 }
2443 }
2444
2445 return result;
2446 }
2447
2448 /*
2449 * get_matching_hash_bounds
2450 * Determine offset of the hash bound matching the specified values,
2451 * considering that all the non-null values come from clauses containing
2452 * a compatible hash equality operator and any keys that are null come
2453 * from an IS NULL clause.
2454 *
2455 * Generally this function will return a single matching bound offset,
2456 * although if a partition has not been setup for a given modulus then we may
2457 * return no matches. If the number of clauses found don't cover the entire
2458 * partition key, then we'll need to return all offsets.
2459 *
2460 * 'opstrategy' if non-zero must be HTEqualStrategyNumber.
2461 *
2462 * 'values' contains Datums indexed by the partition key to use for pruning.
2463 *
2464 * 'nvalues', the number of Datums in the 'values' array.
2465 *
2466 * 'partsupfunc' contains partition hashing functions that can produce correct
2467 * hash for the type of the values contained in 'values'.
2468 *
2469 * 'nullkeys' is the set of partition keys that are null.
2470 */
2471 static PruneStepResult *
get_matching_hash_bounds(PartitionPruneContext * context,StrategyNumber opstrategy,Datum * values,int nvalues,FmgrInfo * partsupfunc,Bitmapset * nullkeys)2472 get_matching_hash_bounds(PartitionPruneContext *context,
2473 StrategyNumber opstrategy, Datum *values, int nvalues,
2474 FmgrInfo *partsupfunc, Bitmapset *nullkeys)
2475 {
2476 PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2477 PartitionBoundInfo boundinfo = context->boundinfo;
2478 int *partindices = boundinfo->indexes;
2479 int partnatts = context->partnatts;
2480 bool isnull[PARTITION_MAX_KEYS];
2481 int i;
2482 uint64 rowHash;
2483 int greatest_modulus;
2484
2485 Assert(context->strategy == PARTITION_STRATEGY_HASH);
2486
2487 /*
2488 * For hash partitioning we can only perform pruning based on equality
2489 * clauses to the partition key or IS NULL clauses. We also can only
2490 * prune if we got values for all keys.
2491 */
2492 if (nvalues + bms_num_members(nullkeys) == partnatts)
2493 {
2494 /*
2495 * If there are any values, they must have come from clauses
2496 * containing an equality operator compatible with hash partitioning.
2497 */
2498 Assert(opstrategy == HTEqualStrategyNumber || nvalues == 0);
2499
2500 for (i = 0; i < partnatts; i++)
2501 isnull[i] = bms_is_member(i, nullkeys);
2502
2503 rowHash = compute_partition_hash_value(partnatts, partsupfunc,
2504 values, isnull);
2505
2506 greatest_modulus = boundinfo->nindexes;
2507 if (partindices[rowHash % greatest_modulus] >= 0)
2508 result->bound_offsets =
2509 bms_make_singleton(rowHash % greatest_modulus);
2510 }
2511 else
2512 {
2513 /* Report all valid offsets into the boundinfo->indexes array. */
2514 result->bound_offsets = bms_add_range(NULL, 0,
2515 boundinfo->nindexes - 1);
2516 }
2517
2518 /*
2519 * There is neither a special hash null partition or the default hash
2520 * partition.
2521 */
2522 result->scan_null = result->scan_default = false;
2523
2524 return result;
2525 }
2526
2527 /*
2528 * get_matching_list_bounds
2529 * Determine the offsets of list bounds matching the specified value,
2530 * according to the semantics of the given operator strategy
2531 *
2532 * scan_default will be set in the returned struct, if the default partition
2533 * needs to be scanned, provided one exists at all. scan_null will be set if
2534 * the special null-accepting partition needs to be scanned.
2535 *
2536 * 'opstrategy' if non-zero must be a btree strategy number.
2537 *
2538 * 'value' contains the value to use for pruning.
2539 *
2540 * 'nvalues', if non-zero, should be exactly 1, because of list partitioning.
2541 *
2542 * 'partsupfunc' contains the list partitioning comparison function to be used
2543 * to perform partition_list_bsearch
2544 *
2545 * 'nullkeys' is the set of partition keys that are null.
2546 */
2547 static PruneStepResult *
get_matching_list_bounds(PartitionPruneContext * context,StrategyNumber opstrategy,Datum value,int nvalues,FmgrInfo * partsupfunc,Bitmapset * nullkeys)2548 get_matching_list_bounds(PartitionPruneContext *context,
2549 StrategyNumber opstrategy, Datum value, int nvalues,
2550 FmgrInfo *partsupfunc, Bitmapset *nullkeys)
2551 {
2552 PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2553 PartitionBoundInfo boundinfo = context->boundinfo;
2554 int off,
2555 minoff,
2556 maxoff;
2557 bool is_equal;
2558 bool inclusive = false;
2559 Oid *partcollation = context->partcollation;
2560
2561 Assert(context->strategy == PARTITION_STRATEGY_LIST);
2562 Assert(context->partnatts == 1);
2563
2564 result->scan_null = result->scan_default = false;
2565
2566 if (!bms_is_empty(nullkeys))
2567 {
2568 /*
2569 * Nulls may exist in only one partition - the partition whose
2570 * accepted set of values includes null or the default partition if
2571 * the former doesn't exist.
2572 */
2573 if (partition_bound_accepts_nulls(boundinfo))
2574 result->scan_null = true;
2575 else
2576 result->scan_default = partition_bound_has_default(boundinfo);
2577 return result;
2578 }
2579
2580 /*
2581 * If there are no datums to compare keys with, but there are partitions,
2582 * just return the default partition if one exists.
2583 */
2584 if (boundinfo->ndatums == 0)
2585 {
2586 result->scan_default = partition_bound_has_default(boundinfo);
2587 return result;
2588 }
2589
2590 minoff = 0;
2591 maxoff = boundinfo->ndatums - 1;
2592
2593 /*
2594 * If there are no values to compare with the datums in boundinfo, it
2595 * means the caller asked for partitions for all non-null datums. Add
2596 * indexes of *all* partitions, including the default if any.
2597 */
2598 if (nvalues == 0)
2599 {
2600 Assert(boundinfo->ndatums > 0);
2601 result->bound_offsets = bms_add_range(NULL, 0,
2602 boundinfo->ndatums - 1);
2603 result->scan_default = partition_bound_has_default(boundinfo);
2604 return result;
2605 }
2606
2607 /* Special case handling of values coming from a <> operator clause. */
2608 if (opstrategy == InvalidStrategy)
2609 {
2610 /*
2611 * First match to all bounds. We'll remove any matching datums below.
2612 */
2613 Assert(boundinfo->ndatums > 0);
2614 result->bound_offsets = bms_add_range(NULL, 0,
2615 boundinfo->ndatums - 1);
2616
2617 off = partition_list_bsearch(partsupfunc, partcollation, boundinfo,
2618 value, &is_equal);
2619 if (off >= 0 && is_equal)
2620 {
2621
2622 /* We have a match. Remove from the result. */
2623 Assert(boundinfo->indexes[off] >= 0);
2624 result->bound_offsets = bms_del_member(result->bound_offsets,
2625 off);
2626 }
2627
2628 /* Always include the default partition if any. */
2629 result->scan_default = partition_bound_has_default(boundinfo);
2630
2631 return result;
2632 }
2633
2634 /*
2635 * With range queries, always include the default list partition, because
2636 * list partitions divide the key space in a discontinuous manner, not all
2637 * values in the given range will have a partition assigned. This may not
2638 * technically be true for some data types (e.g. integer types), however,
2639 * we currently lack any sort of infrastructure to provide us with proofs
2640 * that would allow us to do anything smarter here.
2641 */
2642 if (opstrategy != BTEqualStrategyNumber)
2643 result->scan_default = partition_bound_has_default(boundinfo);
2644
2645 switch (opstrategy)
2646 {
2647 case BTEqualStrategyNumber:
2648 off = partition_list_bsearch(partsupfunc,
2649 partcollation,
2650 boundinfo, value,
2651 &is_equal);
2652 if (off >= 0 && is_equal)
2653 {
2654 Assert(boundinfo->indexes[off] >= 0);
2655 result->bound_offsets = bms_make_singleton(off);
2656 }
2657 else
2658 result->scan_default = partition_bound_has_default(boundinfo);
2659 return result;
2660
2661 case BTGreaterEqualStrategyNumber:
2662 inclusive = true;
2663 /* fall through */
2664 case BTGreaterStrategyNumber:
2665 off = partition_list_bsearch(partsupfunc,
2666 partcollation,
2667 boundinfo, value,
2668 &is_equal);
2669 if (off >= 0)
2670 {
2671 /* We don't want the matched datum to be in the result. */
2672 if (!is_equal || !inclusive)
2673 off++;
2674 }
2675 else
2676 {
2677 /*
2678 * This case means all partition bounds are greater, which in
2679 * turn means that all partitions satisfy this key.
2680 */
2681 off = 0;
2682 }
2683
2684 /*
2685 * off is greater than the numbers of datums we have partitions
2686 * for. The only possible partition that could contain a match is
2687 * the default partition, but we must've set context->scan_default
2688 * above anyway if one exists.
2689 */
2690 if (off > boundinfo->ndatums - 1)
2691 return result;
2692
2693 minoff = off;
2694 break;
2695
2696 case BTLessEqualStrategyNumber:
2697 inclusive = true;
2698 /* fall through */
2699 case BTLessStrategyNumber:
2700 off = partition_list_bsearch(partsupfunc,
2701 partcollation,
2702 boundinfo, value,
2703 &is_equal);
2704 if (off >= 0 && is_equal && !inclusive)
2705 off--;
2706
2707 /*
2708 * off is smaller than the datums of all non-default partitions.
2709 * The only possible partition that could contain a match is the
2710 * default partition, but we must've set context->scan_default
2711 * above anyway if one exists.
2712 */
2713 if (off < 0)
2714 return result;
2715
2716 maxoff = off;
2717 break;
2718
2719 default:
2720 elog(ERROR, "invalid strategy number %d", opstrategy);
2721 break;
2722 }
2723
2724 Assert(minoff >= 0 && maxoff >= 0);
2725 result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
2726 return result;
2727 }
2728
2729
2730 /*
2731 * get_matching_range_datums
2732 * Determine the offsets of range bounds matching the specified values,
2733 * according to the semantics of the given operator strategy
2734 *
2735 * Each datum whose offset is in result is to be treated as the upper bound of
2736 * the partition that will contain the desired values.
2737 *
2738 * scan_default is set in the returned struct if a default partition exists
2739 * and we're absolutely certain that it needs to be scanned. We do *not* set
2740 * it just because values match portions of the key space uncovered by
2741 * partitions other than default (space which we normally assume to belong to
2742 * the default partition): the final set of bounds obtained after combining
2743 * multiple pruning steps might exclude it, so we infer its inclusion
2744 * elsewhere.
2745 *
2746 * 'opstrategy' if non-zero must be a btree strategy number.
2747 *
2748 * 'values' contains Datums indexed by the partition key to use for pruning.
2749 *
2750 * 'nvalues', number of Datums in 'values' array. Must be <= context->partnatts.
2751 *
2752 * 'partsupfunc' contains the range partitioning comparison functions to be
2753 * used to perform partition_range_datum_bsearch or partition_rbound_datum_cmp
2754 * using.
2755 *
2756 * 'nullkeys' is the set of partition keys that are null.
2757 */
2758 static PruneStepResult *
get_matching_range_bounds(PartitionPruneContext * context,StrategyNumber opstrategy,Datum * values,int nvalues,FmgrInfo * partsupfunc,Bitmapset * nullkeys)2759 get_matching_range_bounds(PartitionPruneContext *context,
2760 StrategyNumber opstrategy, Datum *values, int nvalues,
2761 FmgrInfo *partsupfunc, Bitmapset *nullkeys)
2762 {
2763 PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2764 PartitionBoundInfo boundinfo = context->boundinfo;
2765 Oid *partcollation = context->partcollation;
2766 int partnatts = context->partnatts;
2767 int *partindices = boundinfo->indexes;
2768 int off,
2769 minoff,
2770 maxoff;
2771 bool is_equal;
2772 bool inclusive = false;
2773
2774 Assert(context->strategy == PARTITION_STRATEGY_RANGE);
2775 Assert(nvalues <= partnatts);
2776
2777 result->scan_null = result->scan_default = false;
2778
2779 /*
2780 * If there are no datums to compare keys with, or if we got an IS NULL
2781 * clause just return the default partition, if it exists.
2782 */
2783 if (boundinfo->ndatums == 0 || !bms_is_empty(nullkeys))
2784 {
2785 result->scan_default = partition_bound_has_default(boundinfo);
2786 return result;
2787 }
2788
2789 minoff = 0;
2790 maxoff = boundinfo->ndatums;
2791
2792 /*
2793 * If there are no values to compare with the datums in boundinfo, it
2794 * means the caller asked for partitions for all non-null datums. Add
2795 * indexes of *all* partitions, including the default partition if one
2796 * exists.
2797 */
2798 if (nvalues == 0)
2799 {
2800 /* ignore key space not covered by any partitions */
2801 if (partindices[minoff] < 0)
2802 minoff++;
2803 if (partindices[maxoff] < 0)
2804 maxoff--;
2805
2806 result->scan_default = partition_bound_has_default(boundinfo);
2807 Assert(partindices[minoff] >= 0 &&
2808 partindices[maxoff] >= 0);
2809 result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
2810
2811 return result;
2812 }
2813
2814 /*
2815 * If the query does not constrain all key columns, we'll need to scan the
2816 * the default partition, if any.
2817 */
2818 if (nvalues < partnatts)
2819 result->scan_default = partition_bound_has_default(boundinfo);
2820
2821 switch (opstrategy)
2822 {
2823 case BTEqualStrategyNumber:
2824 /* Look for the smallest bound that is = lookup value. */
2825 off = partition_range_datum_bsearch(partsupfunc,
2826 partcollation,
2827 boundinfo,
2828 nvalues, values,
2829 &is_equal);
2830
2831 if (off >= 0 && is_equal)
2832 {
2833 if (nvalues == partnatts)
2834 {
2835 /* There can only be zero or one matching partition. */
2836 result->bound_offsets = bms_make_singleton(off + 1);
2837 return result;
2838 }
2839 else
2840 {
2841 int saved_off = off;
2842
2843 /*
2844 * Since the lookup value contains only a prefix of keys,
2845 * we must find other bounds that may also match the
2846 * prefix. partition_range_datum_bsearch() returns the
2847 * offset of one of them, find others by checking adjacent
2848 * bounds.
2849 */
2850
2851 /*
2852 * First find greatest bound that's smaller than the
2853 * lookup value.
2854 */
2855 while (off >= 1)
2856 {
2857 int32 cmpval;
2858
2859 cmpval =
2860 partition_rbound_datum_cmp(partsupfunc,
2861 partcollation,
2862 boundinfo->datums[off - 1],
2863 boundinfo->kind[off - 1],
2864 values, nvalues);
2865 if (cmpval != 0)
2866 break;
2867 off--;
2868 }
2869
2870 Assert(0 ==
2871 partition_rbound_datum_cmp(partsupfunc,
2872 partcollation,
2873 boundinfo->datums[off],
2874 boundinfo->kind[off],
2875 values, nvalues));
2876
2877 /*
2878 * We can treat 'off' as the offset of the smallest bound
2879 * to be included in the result, if we know it is the
2880 * upper bound of the partition in which the lookup value
2881 * could possibly exist. One case it couldn't is if the
2882 * bound, or precisely the matched portion of its prefix,
2883 * is not inclusive.
2884 */
2885 if (boundinfo->kind[off][nvalues] ==
2886 PARTITION_RANGE_DATUM_MINVALUE)
2887 off++;
2888
2889 minoff = off;
2890
2891 /*
2892 * Now find smallest bound that's greater than the lookup
2893 * value.
2894 */
2895 off = saved_off;
2896 while (off < boundinfo->ndatums - 1)
2897 {
2898 int32 cmpval;
2899
2900 cmpval = partition_rbound_datum_cmp(partsupfunc,
2901 partcollation,
2902 boundinfo->datums[off + 1],
2903 boundinfo->kind[off + 1],
2904 values, nvalues);
2905 if (cmpval != 0)
2906 break;
2907 off++;
2908 }
2909
2910 Assert(0 ==
2911 partition_rbound_datum_cmp(partsupfunc,
2912 partcollation,
2913 boundinfo->datums[off],
2914 boundinfo->kind[off],
2915 values, nvalues));
2916
2917 /*
2918 * off + 1, then would be the offset of the greatest bound
2919 * to be included in the result.
2920 */
2921 maxoff = off + 1;
2922 }
2923
2924 Assert(minoff >= 0 && maxoff >= 0);
2925 result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
2926 }
2927 else
2928 {
2929 /*
2930 * The lookup value falls in the range between some bounds in
2931 * boundinfo. 'off' would be the offset of the greatest bound
2932 * that is <= lookup value, so add off + 1 to the result
2933 * instead as the offset of the upper bound of the only
2934 * partition that may contain the lookup value. If 'off' is
2935 * -1 indicating that all bounds are greater, then we simply
2936 * end up adding the first bound's offset, that is, 0.
2937 */
2938 result->bound_offsets = bms_make_singleton(off + 1);
2939 }
2940
2941 return result;
2942
2943 case BTGreaterEqualStrategyNumber:
2944 inclusive = true;
2945 /* fall through */
2946 case BTGreaterStrategyNumber:
2947
2948 /*
2949 * Look for the smallest bound that is > or >= lookup value and
2950 * set minoff to its offset.
2951 */
2952 off = partition_range_datum_bsearch(partsupfunc,
2953 partcollation,
2954 boundinfo,
2955 nvalues, values,
2956 &is_equal);
2957 if (off < 0)
2958 {
2959 /*
2960 * All bounds are greater than the lookup value, so include
2961 * all of them in the result.
2962 */
2963 minoff = 0;
2964 }
2965 else
2966 {
2967 if (is_equal && nvalues < partnatts)
2968 {
2969 /*
2970 * Since the lookup value contains only a prefix of keys,
2971 * we must find other bounds that may also match the
2972 * prefix. partition_range_datum_bsearch() returns the
2973 * offset of one of them, find others by checking adjacent
2974 * bounds.
2975 *
2976 * Based on whether the lookup values are inclusive or
2977 * not, we must either include the indexes of all such
2978 * bounds in the result (that is, set minoff to the index
2979 * of smallest such bound) or find the smallest one that's
2980 * greater than the lookup values and set minoff to that.
2981 */
2982 while (off >= 1 && off < boundinfo->ndatums - 1)
2983 {
2984 int32 cmpval;
2985 int nextoff;
2986
2987 nextoff = inclusive ? off - 1 : off + 1;
2988 cmpval =
2989 partition_rbound_datum_cmp(partsupfunc,
2990 partcollation,
2991 boundinfo->datums[nextoff],
2992 boundinfo->kind[nextoff],
2993 values, nvalues);
2994 if (cmpval != 0)
2995 break;
2996
2997 off = nextoff;
2998 }
2999
3000 Assert(0 ==
3001 partition_rbound_datum_cmp(partsupfunc,
3002 partcollation,
3003 boundinfo->datums[off],
3004 boundinfo->kind[off],
3005 values, nvalues));
3006
3007 minoff = inclusive ? off : off + 1;
3008 }
3009 else
3010 {
3011
3012 /*
3013 * lookup value falls in the range between some bounds in
3014 * boundinfo. off would be the offset of the greatest
3015 * bound that is <= lookup value, so add off + 1 to the
3016 * result instead as the offset of the upper bound of the
3017 * smallest partition that may contain the lookup value.
3018 */
3019 minoff = off + 1;
3020 }
3021 }
3022 break;
3023
3024 case BTLessEqualStrategyNumber:
3025 inclusive = true;
3026 /* fall through */
3027 case BTLessStrategyNumber:
3028
3029 /*
3030 * Look for the greatest bound that is < or <= lookup value and
3031 * set maxoff to its offset.
3032 */
3033 off = partition_range_datum_bsearch(partsupfunc,
3034 partcollation,
3035 boundinfo,
3036 nvalues, values,
3037 &is_equal);
3038 if (off >= 0)
3039 {
3040 /*
3041 * See the comment above.
3042 */
3043 if (is_equal && nvalues < partnatts)
3044 {
3045 while (off >= 1 && off < boundinfo->ndatums - 1)
3046 {
3047 int32 cmpval;
3048 int nextoff;
3049
3050 nextoff = inclusive ? off + 1 : off - 1;
3051 cmpval = partition_rbound_datum_cmp(partsupfunc,
3052 partcollation,
3053 boundinfo->datums[nextoff],
3054 boundinfo->kind[nextoff],
3055 values, nvalues);
3056 if (cmpval != 0)
3057 break;
3058
3059 off = nextoff;
3060 }
3061
3062 Assert(0 ==
3063 partition_rbound_datum_cmp(partsupfunc,
3064 partcollation,
3065 boundinfo->datums[off],
3066 boundinfo->kind[off],
3067 values, nvalues));
3068
3069 maxoff = inclusive ? off + 1 : off;
3070 }
3071
3072 /*
3073 * The lookup value falls in the range between some bounds in
3074 * boundinfo. 'off' would be the offset of the greatest bound
3075 * that is <= lookup value, so add off + 1 to the result
3076 * instead as the offset of the upper bound of the greatest
3077 * partition that may contain lookup value. If the lookup
3078 * value had exactly matched the bound, but it isn't
3079 * inclusive, no need add the adjacent partition.
3080 */
3081 else if (!is_equal || inclusive)
3082 maxoff = off + 1;
3083 else
3084 maxoff = off;
3085 }
3086 else
3087 {
3088 /*
3089 * 'off' is -1 indicating that all bounds are greater, so just
3090 * set the first bound's offset as maxoff.
3091 */
3092 maxoff = off + 1;
3093 }
3094 break;
3095
3096 default:
3097 elog(ERROR, "invalid strategy number %d", opstrategy);
3098 break;
3099 }
3100
3101 Assert(minoff >= 0 && minoff <= boundinfo->ndatums);
3102 Assert(maxoff >= 0 && maxoff <= boundinfo->ndatums);
3103
3104 /*
3105 * If the smallest partition to return has MINVALUE (negative infinity) as
3106 * its lower bound, increment it to point to the next finite bound
3107 * (supposedly its upper bound), so that we don't advertently end up
3108 * scanning the default partition.
3109 */
3110 if (minoff < boundinfo->ndatums && partindices[minoff] < 0)
3111 {
3112 int lastkey = nvalues - 1;
3113
3114 if (boundinfo->kind[minoff][lastkey] ==
3115 PARTITION_RANGE_DATUM_MINVALUE)
3116 {
3117 minoff++;
3118 Assert(boundinfo->indexes[minoff] >= 0);
3119 }
3120 }
3121
3122 /*
3123 * If the previous greatest partition has MAXVALUE (positive infinity) as
3124 * its upper bound (something only possible to do with multi-column range
3125 * partitioning), we scan switch to it as the greatest partition to
3126 * return. Again, so that we don't advertently end up scanning the
3127 * default partition.
3128 */
3129 if (maxoff >= 1 && partindices[maxoff] < 0)
3130 {
3131 int lastkey = nvalues - 1;
3132
3133 if (boundinfo->kind[maxoff - 1][lastkey] ==
3134 PARTITION_RANGE_DATUM_MAXVALUE)
3135 {
3136 maxoff--;
3137 Assert(boundinfo->indexes[maxoff] >= 0);
3138 }
3139 }
3140
3141 Assert(minoff >= 0 && maxoff >= 0);
3142 if (minoff <= maxoff)
3143 result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3144
3145 return result;
3146 }
3147
3148 /*
3149 * pull_exec_paramids
3150 * Returns a Bitmapset containing the paramids of all Params with
3151 * paramkind = PARAM_EXEC in 'expr'.
3152 */
3153 static Bitmapset *
pull_exec_paramids(Expr * expr)3154 pull_exec_paramids(Expr *expr)
3155 {
3156 Bitmapset *result = NULL;
3157
3158 (void) pull_exec_paramids_walker((Node *) expr, &result);
3159
3160 return result;
3161 }
3162
3163 static bool
pull_exec_paramids_walker(Node * node,Bitmapset ** context)3164 pull_exec_paramids_walker(Node *node, Bitmapset **context)
3165 {
3166 if (node == NULL)
3167 return false;
3168 if (IsA(node, Param))
3169 {
3170 Param *param = (Param *) node;
3171
3172 if (param->paramkind == PARAM_EXEC)
3173 *context = bms_add_member(*context, param->paramid);
3174 return false;
3175 }
3176 return expression_tree_walker(node, pull_exec_paramids_walker,
3177 (void *) context);
3178 }
3179
3180 /*
3181 * get_partkey_exec_paramids
3182 * Loop through given pruning steps and find out which exec Params
3183 * are used.
3184 *
3185 * Returns a Bitmapset of Param IDs.
3186 */
3187 static Bitmapset *
get_partkey_exec_paramids(List * steps)3188 get_partkey_exec_paramids(List *steps)
3189 {
3190 Bitmapset *execparamids = NULL;
3191 ListCell *lc;
3192
3193 foreach(lc, steps)
3194 {
3195 PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc);
3196 ListCell *lc2;
3197
3198 if (!IsA(step, PartitionPruneStepOp))
3199 continue;
3200
3201 foreach(lc2, step->exprs)
3202 {
3203 Expr *expr = lfirst(lc2);
3204
3205 /* We can be quick for plain Consts */
3206 if (!IsA(expr, Const))
3207 execparamids = bms_join(execparamids,
3208 pull_exec_paramids(expr));
3209 }
3210 }
3211
3212 return execparamids;
3213 }
3214
3215 /*
3216 * perform_pruning_base_step
3217 * Determines the indexes of datums that satisfy conditions specified in
3218 * 'opstep'.
3219 *
3220 * Result also contains whether special null-accepting and/or default
3221 * partition need to be scanned.
3222 */
3223 static PruneStepResult *
perform_pruning_base_step(PartitionPruneContext * context,PartitionPruneStepOp * opstep)3224 perform_pruning_base_step(PartitionPruneContext *context,
3225 PartitionPruneStepOp *opstep)
3226 {
3227 ListCell *lc1,
3228 *lc2;
3229 int keyno,
3230 nvalues;
3231 Datum values[PARTITION_MAX_KEYS];
3232 FmgrInfo *partsupfunc;
3233 int stateidx;
3234
3235 /*
3236 * There better be the same number of expressions and compare functions.
3237 */
3238 Assert(list_length(opstep->exprs) == list_length(opstep->cmpfns));
3239
3240 nvalues = 0;
3241 lc1 = list_head(opstep->exprs);
3242 lc2 = list_head(opstep->cmpfns);
3243
3244 /*
3245 * Generate the partition lookup key that will be used by one of the
3246 * get_matching_*_bounds functions called below.
3247 */
3248 for (keyno = 0; keyno < context->partnatts; keyno++)
3249 {
3250 /*
3251 * For hash partitioning, it is possible that values of some keys are
3252 * not provided in operator clauses, but instead the planner found
3253 * that they appeared in a IS NULL clause.
3254 */
3255 if (bms_is_member(keyno, opstep->nullkeys))
3256 continue;
3257
3258 /*
3259 * For range partitioning, we must only perform pruning with values
3260 * for either all partition keys or a prefix thereof.
3261 */
3262 if (keyno > nvalues && context->strategy == PARTITION_STRATEGY_RANGE)
3263 break;
3264
3265 if (lc1 != NULL)
3266 {
3267 Expr *expr;
3268 Datum datum;
3269 bool isnull;
3270 Oid cmpfn;
3271
3272 expr = lfirst(lc1);
3273 stateidx = PruneCxtStateIdx(context->partnatts,
3274 opstep->step.step_id, keyno);
3275 partkey_datum_from_expr(context, expr, stateidx,
3276 &datum, &isnull);
3277
3278 /*
3279 * Since we only allow strict operators in pruning steps, any
3280 * null-valued comparison value must cause the comparison to fail,
3281 * so that no partitions could match.
3282 */
3283 if (isnull)
3284 {
3285 PruneStepResult *result;
3286
3287 result = (PruneStepResult *) palloc(sizeof(PruneStepResult));
3288 result->bound_offsets = NULL;
3289 result->scan_default = false;
3290 result->scan_null = false;
3291
3292 return result;
3293 }
3294
3295 /* Set up the stepcmpfuncs entry, unless we already did */
3296 cmpfn = lfirst_oid(lc2);
3297 Assert(OidIsValid(cmpfn));
3298 if (cmpfn != context->stepcmpfuncs[stateidx].fn_oid)
3299 {
3300 /*
3301 * If the needed support function is the same one cached in
3302 * the relation's partition key, copy the cached FmgrInfo.
3303 * Otherwise (i.e., when we have a cross-type comparison), an
3304 * actual lookup is required.
3305 */
3306 if (cmpfn == context->partsupfunc[keyno].fn_oid)
3307 fmgr_info_copy(&context->stepcmpfuncs[stateidx],
3308 &context->partsupfunc[keyno],
3309 context->ppccontext);
3310 else
3311 fmgr_info_cxt(cmpfn, &context->stepcmpfuncs[stateidx],
3312 context->ppccontext);
3313 }
3314
3315 values[keyno] = datum;
3316 nvalues++;
3317
3318 lc1 = lnext(lc1);
3319 lc2 = lnext(lc2);
3320 }
3321 }
3322
3323 /*
3324 * Point partsupfunc to the entry for the 0th key of this step; the
3325 * additional support functions, if any, follow consecutively.
3326 */
3327 stateidx = PruneCxtStateIdx(context->partnatts, opstep->step.step_id, 0);
3328 partsupfunc = &context->stepcmpfuncs[stateidx];
3329
3330 switch (context->strategy)
3331 {
3332 case PARTITION_STRATEGY_HASH:
3333 return get_matching_hash_bounds(context,
3334 opstep->opstrategy,
3335 values, nvalues,
3336 partsupfunc,
3337 opstep->nullkeys);
3338
3339 case PARTITION_STRATEGY_LIST:
3340 return get_matching_list_bounds(context,
3341 opstep->opstrategy,
3342 values[0], nvalues,
3343 &partsupfunc[0],
3344 opstep->nullkeys);
3345
3346 case PARTITION_STRATEGY_RANGE:
3347 return get_matching_range_bounds(context,
3348 opstep->opstrategy,
3349 values, nvalues,
3350 partsupfunc,
3351 opstep->nullkeys);
3352
3353 default:
3354 elog(ERROR, "unexpected partition strategy: %d",
3355 (int) context->strategy);
3356 break;
3357 }
3358
3359 return NULL;
3360 }
3361
3362 /*
3363 * perform_pruning_combine_step
3364 * Determines the indexes of datums obtained by combining those given
3365 * by the steps identified by cstep->source_stepids using the specified
3366 * combination method
3367 *
3368 * Since cstep may refer to the result of earlier steps, we also receive
3369 * step_results here.
3370 */
3371 static PruneStepResult *
perform_pruning_combine_step(PartitionPruneContext * context,PartitionPruneStepCombine * cstep,PruneStepResult ** step_results)3372 perform_pruning_combine_step(PartitionPruneContext *context,
3373 PartitionPruneStepCombine *cstep,
3374 PruneStepResult **step_results)
3375 {
3376 PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
3377 bool firststep;
3378 ListCell *lc1;
3379
3380 /*
3381 * A combine step without any source steps is an indication to not perform
3382 * any partition pruning. Return all datum indexes in that case.
3383 */
3384 if (cstep->source_stepids == NIL)
3385 {
3386 PartitionBoundInfo boundinfo = context->boundinfo;
3387
3388 result->bound_offsets =
3389 bms_add_range(NULL, 0, boundinfo->nindexes - 1);
3390 result->scan_default = partition_bound_has_default(boundinfo);
3391 result->scan_null = partition_bound_accepts_nulls(boundinfo);
3392 return result;
3393 }
3394
3395 switch (cstep->combineOp)
3396 {
3397 case PARTPRUNE_COMBINE_UNION:
3398 foreach(lc1, cstep->source_stepids)
3399 {
3400 int step_id = lfirst_int(lc1);
3401 PruneStepResult *step_result;
3402
3403 /*
3404 * step_results[step_id] must contain a valid result, which is
3405 * confirmed by the fact that cstep's step_id is greater than
3406 * step_id and the fact that results of the individual steps
3407 * are evaluated in sequence of their step_ids.
3408 */
3409 if (step_id >= cstep->step.step_id)
3410 elog(ERROR, "invalid pruning combine step argument");
3411 step_result = step_results[step_id];
3412 Assert(step_result != NULL);
3413
3414 /* Record any additional datum indexes from this step */
3415 result->bound_offsets = bms_add_members(result->bound_offsets,
3416 step_result->bound_offsets);
3417
3418 /* Update whether to scan null and default partitions. */
3419 if (!result->scan_null)
3420 result->scan_null = step_result->scan_null;
3421 if (!result->scan_default)
3422 result->scan_default = step_result->scan_default;
3423 }
3424 break;
3425
3426 case PARTPRUNE_COMBINE_INTERSECT:
3427 firststep = true;
3428 foreach(lc1, cstep->source_stepids)
3429 {
3430 int step_id = lfirst_int(lc1);
3431 PruneStepResult *step_result;
3432
3433 if (step_id >= cstep->step.step_id)
3434 elog(ERROR, "invalid pruning combine step argument");
3435 step_result = step_results[step_id];
3436 Assert(step_result != NULL);
3437
3438 if (firststep)
3439 {
3440 /* Copy step's result the first time. */
3441 result->bound_offsets =
3442 bms_copy(step_result->bound_offsets);
3443 result->scan_null = step_result->scan_null;
3444 result->scan_default = step_result->scan_default;
3445 firststep = false;
3446 }
3447 else
3448 {
3449 /* Record datum indexes common to both steps */
3450 result->bound_offsets =
3451 bms_int_members(result->bound_offsets,
3452 step_result->bound_offsets);
3453
3454 /* Update whether to scan null and default partitions. */
3455 if (result->scan_null)
3456 result->scan_null = step_result->scan_null;
3457 if (result->scan_default)
3458 result->scan_default = step_result->scan_default;
3459 }
3460 }
3461 break;
3462 }
3463
3464 return result;
3465 }
3466
3467 /*
3468 * match_boolean_partition_clause
3469 *
3470 * If we're able to match the clause to the partition key as specially-shaped
3471 * boolean clause, set *outconst to a Const containing a true or false value
3472 * and return PARTCLAUSE_MATCH_CLAUSE. Returns PARTCLAUSE_UNSUPPORTED if the
3473 * clause is not a boolean clause or if the boolean clause is unsuitable for
3474 * partition pruning. Returns PARTCLAUSE_NOMATCH if it's a bool quals but
3475 * just does not match this partition key. *outconst is set to NULL in the
3476 * latter two cases.
3477 */
3478 static PartClauseMatchStatus
match_boolean_partition_clause(Oid partopfamily,Expr * clause,Expr * partkey,Expr ** outconst)3479 match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
3480 Expr **outconst)
3481 {
3482 Expr *leftop;
3483
3484 *outconst = NULL;
3485
3486 if (!IsBooleanOpfamily(partopfamily))
3487 return PARTCLAUSE_UNSUPPORTED;
3488
3489 if (IsA(clause, BooleanTest))
3490 {
3491 BooleanTest *btest = (BooleanTest *) clause;
3492
3493 /* Only IS [NOT] TRUE/FALSE are any good to us */
3494 if (btest->booltesttype == IS_UNKNOWN ||
3495 btest->booltesttype == IS_NOT_UNKNOWN)
3496 return PARTCLAUSE_UNSUPPORTED;
3497
3498 leftop = btest->arg;
3499 if (IsA(leftop, RelabelType))
3500 leftop = ((RelabelType *) leftop)->arg;
3501
3502 if (equal(leftop, partkey))
3503 *outconst = (btest->booltesttype == IS_TRUE ||
3504 btest->booltesttype == IS_NOT_FALSE)
3505 ? (Expr *) makeBoolConst(true, false)
3506 : (Expr *) makeBoolConst(false, false);
3507
3508 if (*outconst)
3509 return PARTCLAUSE_MATCH_CLAUSE;
3510 }
3511 else
3512 {
3513 bool is_not_clause = not_clause((Node *) clause);
3514
3515 leftop = is_not_clause ? get_notclausearg(clause) : clause;
3516
3517 if (IsA(leftop, RelabelType))
3518 leftop = ((RelabelType *) leftop)->arg;
3519
3520 /* Compare to the partition key, and make up a clause ... */
3521 if (equal(leftop, partkey))
3522 *outconst = is_not_clause ?
3523 (Expr *) makeBoolConst(false, false) :
3524 (Expr *) makeBoolConst(true, false);
3525 else if (equal(negate_clause((Node *) leftop), partkey))
3526 *outconst = (Expr *) makeBoolConst(false, false);
3527
3528 if (*outconst)
3529 return PARTCLAUSE_MATCH_CLAUSE;
3530 }
3531
3532 return PARTCLAUSE_NOMATCH;
3533 }
3534
3535 /*
3536 * partkey_datum_from_expr
3537 * Evaluate expression for potential partition pruning
3538 *
3539 * Evaluate 'expr'; set *value and *isnull to the resulting Datum and nullflag.
3540 *
3541 * If expr isn't a Const, its ExprState is in stateidx of the context
3542 * exprstate array.
3543 *
3544 * Note that the evaluated result may be in the per-tuple memory context of
3545 * context->planstate->ps_ExprContext, and we may have leaked other memory
3546 * there too. This memory must be recovered by resetting that ExprContext
3547 * after we're done with the pruning operation (see execPartition.c).
3548 */
3549 static void
partkey_datum_from_expr(PartitionPruneContext * context,Expr * expr,int stateidx,Datum * value,bool * isnull)3550 partkey_datum_from_expr(PartitionPruneContext *context,
3551 Expr *expr, int stateidx,
3552 Datum *value, bool *isnull)
3553 {
3554 if (IsA(expr, Const))
3555 {
3556 /* We can always determine the value of a constant */
3557 Const *con = (Const *) expr;
3558
3559 *value = con->constvalue;
3560 *isnull = con->constisnull;
3561 }
3562 else
3563 {
3564 ExprState *exprstate;
3565 ExprContext *ectx;
3566
3567 /*
3568 * We should never see a non-Const in a step unless we're running in
3569 * the executor.
3570 */
3571 Assert(context->planstate != NULL);
3572
3573 exprstate = context->exprstates[stateidx];
3574 ectx = context->planstate->ps_ExprContext;
3575 *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
3576 }
3577 }
3578