Harmonize parameter names in ecpg code.
[pgsql.git] / src / backend / partitioning / partprune.c
blob5ab46123671773e7408404451f02b50256727fae
1 /*-------------------------------------------------------------------------
3 * partprune.c
4 * Support for partition pruning during query planning and execution
6 * This module implements partition pruning using the information contained in
7 * a table's partition descriptor, query clauses, and run-time parameters.
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.
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.
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.
26 * See gen_partprune_steps_internal() for more details on step generation.
28 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
29 * Portions Copyright (c) 1994, Regents of the University of California
31 * IDENTIFICATION
32 * src/backend/partitioning/partprune.c
34 *-------------------------------------------------------------------------
36 #include "postgres.h"
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/appendinfo.h"
49 #include "optimizer/cost.h"
50 #include "optimizer/optimizer.h"
51 #include "optimizer/pathnode.h"
52 #include "parser/parsetree.h"
53 #include "partitioning/partbounds.h"
54 #include "partitioning/partprune.h"
55 #include "rewrite/rewriteManip.h"
56 #include "utils/array.h"
57 #include "utils/lsyscache.h"
61 * Information about a clause matched with a partition key.
63 typedef struct PartClauseInfo
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;
75 * PartClauseMatchStatus
76 * Describes the result of match_clause_to_partition_key()
78 typedef enum PartClauseMatchStatus
80 PARTCLAUSE_NOMATCH,
81 PARTCLAUSE_MATCH_CLAUSE,
82 PARTCLAUSE_MATCH_NULLNESS,
83 PARTCLAUSE_MATCH_STEPS,
84 PARTCLAUSE_MATCH_CONTRADICT,
85 PARTCLAUSE_UNSUPPORTED
86 } PartClauseMatchStatus;
89 * PartClauseTarget
90 * Identifies which qual clauses we can use for generating pruning steps
92 typedef enum PartClauseTarget
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;
100 * GeneratePruningStepsContext
101 * Information about the current state of generation of "pruning steps"
102 * for a given set of clauses
104 * gen_partprune_steps() initializes and returns an instance of this struct.
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.
111 typedef struct GeneratePruningStepsContext
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;
127 /* The result of performing one PartitionPruneStep */
128 typedef struct PruneStepResult
131 * The offsets of bounds (in a table's boundinfo) whose partition is
132 * selected by the pruning step.
134 Bitmapset *bound_offsets;
136 bool scan_default; /* Scan the default partition? */
137 bool scan_null; /* Scan the partition for NULL values? */
138 } PruneStepResult;
141 static List *add_part_relids(List *allpartrelids, Bitmapset *partrelids);
142 static List *make_partitionedrel_pruneinfo(PlannerInfo *root,
143 RelOptInfo *parentrel,
144 List *prunequal,
145 Bitmapset *partrelids,
146 int *relid_subplan_map,
147 Bitmapset **matchedsubplans);
148 static void gen_partprune_steps(RelOptInfo *rel, List *clauses,
149 PartClauseTarget target,
150 GeneratePruningStepsContext *context);
151 static List *gen_partprune_steps_internal(GeneratePruningStepsContext *context,
152 List *clauses);
153 static PartitionPruneStep *gen_prune_step_op(GeneratePruningStepsContext *context,
154 StrategyNumber opstrategy, bool op_is_ne,
155 List *exprs, List *cmpfns, Bitmapset *nullkeys);
156 static PartitionPruneStep *gen_prune_step_combine(GeneratePruningStepsContext *context,
157 List *source_stepids,
158 PartitionPruneCombineOp combineOp);
159 static List *gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
160 List **keyclauses, Bitmapset *nullkeys);
161 static PartClauseMatchStatus match_clause_to_partition_key(GeneratePruningStepsContext *context,
162 Expr *clause, Expr *partkey, int partkeyidx,
163 bool *clause_is_not_null,
164 PartClauseInfo **pc, List **clause_steps);
165 static List *get_steps_using_prefix(GeneratePruningStepsContext *context,
166 StrategyNumber step_opstrategy,
167 bool step_op_is_ne,
168 Expr *step_lastexpr,
169 Oid step_lastcmpfn,
170 int step_lastkeyno,
171 Bitmapset *step_nullkeys,
172 List *prefix);
173 static List *get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
174 StrategyNumber step_opstrategy,
175 bool step_op_is_ne,
176 Expr *step_lastexpr,
177 Oid step_lastcmpfn,
178 int step_lastkeyno,
179 Bitmapset *step_nullkeys,
180 List *prefix,
181 ListCell *start,
182 List *step_exprs,
183 List *step_cmpfns);
184 static PruneStepResult *get_matching_hash_bounds(PartitionPruneContext *context,
185 StrategyNumber opstrategy, Datum *values, int nvalues,
186 FmgrInfo *partsupfunc, Bitmapset *nullkeys);
187 static PruneStepResult *get_matching_list_bounds(PartitionPruneContext *context,
188 StrategyNumber opstrategy, Datum value, int nvalues,
189 FmgrInfo *partsupfunc, Bitmapset *nullkeys);
190 static PruneStepResult *get_matching_range_bounds(PartitionPruneContext *context,
191 StrategyNumber opstrategy, Datum *values, int nvalues,
192 FmgrInfo *partsupfunc, Bitmapset *nullkeys);
193 static Bitmapset *pull_exec_paramids(Expr *expr);
194 static bool pull_exec_paramids_walker(Node *node, Bitmapset **context);
195 static Bitmapset *get_partkey_exec_paramids(List *steps);
196 static PruneStepResult *perform_pruning_base_step(PartitionPruneContext *context,
197 PartitionPruneStepOp *opstep);
198 static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *context,
199 PartitionPruneStepCombine *cstep,
200 PruneStepResult **step_results);
201 static PartClauseMatchStatus match_boolean_partition_clause(Oid partopfamily,
202 Expr *clause,
203 Expr *partkey,
204 Expr **outconst);
205 static void partkey_datum_from_expr(PartitionPruneContext *context,
206 Expr *expr, int stateidx,
207 Datum *value, bool *isnull);
211 * make_partition_pruneinfo
212 * Builds a PartitionPruneInfo which can be used in the executor to allow
213 * additional partition pruning to take place. Returns NULL when
214 * partition pruning would be useless.
216 * 'parentrel' is the RelOptInfo for an appendrel, and 'subpaths' is the list
217 * of scan paths for its child rels.
218 * 'prunequal' is a list of potential pruning quals (i.e., restriction
219 * clauses that are applicable to the appendrel).
221 PartitionPruneInfo *
222 make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
223 List *subpaths,
224 List *prunequal)
226 PartitionPruneInfo *pruneinfo;
227 Bitmapset *allmatchedsubplans = NULL;
228 List *allpartrelids;
229 List *prunerelinfos;
230 int *relid_subplan_map;
231 ListCell *lc;
232 int i;
235 * Scan the subpaths to see which ones are scans of partition child
236 * relations, and identify their parent partitioned rels. (Note: we must
237 * restrict the parent partitioned rels to be parentrel or children of
238 * parentrel, otherwise we couldn't translate prunequal to match.)
240 * Also construct a temporary array to map from partition-child-relation
241 * relid to the index in 'subpaths' of the scan plan for that partition.
242 * (Use of "subplan" rather than "subpath" is a bit of a misnomer, but
243 * we'll let it stand.) For convenience, we use 1-based indexes here, so
244 * that zero can represent an un-filled array entry.
246 allpartrelids = NIL;
247 relid_subplan_map = palloc0(sizeof(int) * root->simple_rel_array_size);
249 i = 1;
250 foreach(lc, subpaths)
252 Path *path = (Path *) lfirst(lc);
253 RelOptInfo *pathrel = path->parent;
255 /* We don't consider partitioned joins here */
256 if (pathrel->reloptkind == RELOPT_OTHER_MEMBER_REL)
258 RelOptInfo *prel = pathrel;
259 Bitmapset *partrelids = NULL;
262 * Traverse up to the pathrel's topmost partitioned parent,
263 * collecting parent relids as we go; but stop if we reach
264 * parentrel. (Normally, a pathrel's topmost partitioned parent
265 * is either parentrel or a UNION ALL appendrel child of
266 * parentrel. But when handling partitionwise joins of
267 * multi-level partitioning trees, we can see an append path whose
268 * parentrel is an intermediate partitioned table.)
272 AppendRelInfo *appinfo;
274 Assert(prel->relid < root->simple_rel_array_size);
275 appinfo = root->append_rel_array[prel->relid];
276 prel = find_base_rel(root, appinfo->parent_relid);
277 if (!IS_PARTITIONED_REL(prel))
278 break; /* reached a non-partitioned parent */
279 /* accept this level as an interesting parent */
280 partrelids = bms_add_member(partrelids, prel->relid);
281 if (prel == parentrel)
282 break; /* don't traverse above parentrel */
283 } while (prel->reloptkind == RELOPT_OTHER_MEMBER_REL);
285 if (partrelids)
288 * Found some relevant parent partitions, which may or may not
289 * overlap with partition trees we already found. Add new
290 * information to the allpartrelids list.
292 allpartrelids = add_part_relids(allpartrelids, partrelids);
293 /* Also record the subplan in relid_subplan_map[] */
294 /* No duplicates please */
295 Assert(relid_subplan_map[pathrel->relid] == 0);
296 relid_subplan_map[pathrel->relid] = i;
299 i++;
303 * We now build a PartitionedRelPruneInfo for each topmost partitioned rel
304 * (omitting any that turn out not to have useful pruning quals).
306 prunerelinfos = NIL;
307 foreach(lc, allpartrelids)
309 Bitmapset *partrelids = (Bitmapset *) lfirst(lc);
310 List *pinfolist;
311 Bitmapset *matchedsubplans = NULL;
313 pinfolist = make_partitionedrel_pruneinfo(root, parentrel,
314 prunequal,
315 partrelids,
316 relid_subplan_map,
317 &matchedsubplans);
319 /* When pruning is possible, record the matched subplans */
320 if (pinfolist != NIL)
322 prunerelinfos = lappend(prunerelinfos, pinfolist);
323 allmatchedsubplans = bms_join(matchedsubplans,
324 allmatchedsubplans);
328 pfree(relid_subplan_map);
331 * If none of the partition hierarchies had any useful run-time pruning
332 * quals, then we can just not bother with run-time pruning.
334 if (prunerelinfos == NIL)
335 return NULL;
337 /* Else build the result data structure */
338 pruneinfo = makeNode(PartitionPruneInfo);
339 pruneinfo->prune_infos = prunerelinfos;
342 * Some subplans may not belong to any of the identified partitioned rels.
343 * This can happen for UNION ALL queries which include a non-partitioned
344 * table, or when some of the hierarchies aren't run-time prunable. Build
345 * a bitmapset of the indexes of all such subplans, so that the executor
346 * can identify which subplans should never be pruned.
348 if (bms_num_members(allmatchedsubplans) < list_length(subpaths))
350 Bitmapset *other_subplans;
352 /* Create the complement of allmatchedsubplans */
353 other_subplans = bms_add_range(NULL, 0, list_length(subpaths) - 1);
354 other_subplans = bms_del_members(other_subplans, allmatchedsubplans);
356 pruneinfo->other_subplans = other_subplans;
358 else
359 pruneinfo->other_subplans = NULL;
361 return pruneinfo;
365 * add_part_relids
366 * Add new info to a list of Bitmapsets of partitioned relids.
368 * Within 'allpartrelids', there is one Bitmapset for each topmost parent
369 * partitioned rel. Each Bitmapset contains the RT indexes of the topmost
370 * parent as well as its relevant non-leaf child partitions. Since (by
371 * construction of the rangetable list) parent partitions must have lower
372 * RT indexes than their children, we can distinguish the topmost parent
373 * as being the lowest set bit in the Bitmapset.
375 * 'partrelids' contains the RT indexes of a parent partitioned rel, and
376 * possibly some non-leaf children, that are newly identified as parents of
377 * some subpath rel passed to make_partition_pruneinfo(). These are added
378 * to an appropriate member of 'allpartrelids'.
380 * Note that the list contains only RT indexes of partitioned tables that
381 * are parents of some scan-level relation appearing in the 'subpaths' that
382 * make_partition_pruneinfo() is dealing with. Also, "topmost" parents are
383 * not allowed to be higher than the 'parentrel' associated with the append
384 * path. In this way, we avoid expending cycles on partitioned rels that
385 * can't contribute useful pruning information for the problem at hand.
386 * (It is possible for 'parentrel' to be a child partitioned table, and it
387 * is also possible for scan-level relations to be child partitioned tables
388 * rather than leaf partitions. Hence we must construct this relation set
389 * with reference to the particular append path we're dealing with, rather
390 * than looking at the full partitioning structure represented in the
391 * RelOptInfos.)
393 static List *
394 add_part_relids(List *allpartrelids, Bitmapset *partrelids)
396 Index targetpart;
397 ListCell *lc;
399 /* We can easily get the lowest set bit this way: */
400 targetpart = bms_next_member(partrelids, -1);
401 Assert(targetpart > 0);
403 /* Look for a matching topmost parent */
404 foreach(lc, allpartrelids)
406 Bitmapset *currpartrelids = (Bitmapset *) lfirst(lc);
407 Index currtarget = bms_next_member(currpartrelids, -1);
409 if (targetpart == currtarget)
411 /* Found a match, so add any new RT indexes to this hierarchy */
412 currpartrelids = bms_add_members(currpartrelids, partrelids);
413 lfirst(lc) = currpartrelids;
414 return allpartrelids;
417 /* No match, so add the new partition hierarchy to the list */
418 return lappend(allpartrelids, partrelids);
422 * make_partitionedrel_pruneinfo
423 * Build a List of PartitionedRelPruneInfos, one for each interesting
424 * partitioned rel in a partitioning hierarchy. These can be used in the
425 * executor to allow additional partition pruning to take place.
427 * parentrel: rel associated with the appendpath being considered
428 * prunequal: potential pruning quals, represented for parentrel
429 * partrelids: Set of RT indexes identifying relevant partitioned tables
430 * within a single partitioning hierarchy
431 * relid_subplan_map[]: maps child relation relids to subplan indexes
432 * matchedsubplans: on success, receives the set of subplan indexes which
433 * were matched to this partition hierarchy
435 * If we cannot find any useful run-time pruning steps, return NIL.
436 * However, on success, each rel identified in partrelids will have
437 * an element in the result list, even if some of them are useless.
439 static List *
440 make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
441 List *prunequal,
442 Bitmapset *partrelids,
443 int *relid_subplan_map,
444 Bitmapset **matchedsubplans)
446 RelOptInfo *targetpart = NULL;
447 List *pinfolist = NIL;
448 bool doruntimeprune = false;
449 int *relid_subpart_map;
450 Bitmapset *subplansfound = NULL;
451 ListCell *lc;
452 int rti;
453 int i;
456 * Examine each partitioned rel, constructing a temporary array to map
457 * from planner relids to index of the partitioned rel, and building a
458 * PartitionedRelPruneInfo for each partitioned rel.
460 * In this phase we discover whether runtime pruning is needed at all; if
461 * not, we can avoid doing further work.
463 relid_subpart_map = palloc0(sizeof(int) * root->simple_rel_array_size);
465 i = 1;
466 rti = -1;
467 while ((rti = bms_next_member(partrelids, rti)) > 0)
469 RelOptInfo *subpart = find_base_rel(root, rti);
470 PartitionedRelPruneInfo *pinfo;
471 List *partprunequal;
472 List *initial_pruning_steps;
473 List *exec_pruning_steps;
474 Bitmapset *execparamids;
475 GeneratePruningStepsContext context;
478 * Fill the mapping array.
480 * relid_subpart_map maps relid of a non-leaf partition to the index
481 * in the returned PartitionedRelPruneInfo list of the info for that
482 * partition. We use 1-based indexes here, so that zero can represent
483 * an un-filled array entry.
485 Assert(rti < root->simple_rel_array_size);
486 relid_subpart_map[rti] = i++;
489 * Translate pruning qual, if necessary, for this partition.
491 * The first item in the list is the target partitioned relation.
493 if (!targetpart)
495 targetpart = subpart;
498 * The prunequal is presented to us as a qual for 'parentrel'.
499 * Frequently this rel is the same as targetpart, so we can skip
500 * an adjust_appendrel_attrs step. But it might not be, and then
501 * we have to translate. We update the prunequal parameter here,
502 * because in later iterations of the loop for child partitions,
503 * we want to translate from parent to child variables.
505 if (!bms_equal(parentrel->relids, subpart->relids))
507 int nappinfos;
508 AppendRelInfo **appinfos = find_appinfos_by_relids(root,
509 subpart->relids,
510 &nappinfos);
512 prunequal = (List *) adjust_appendrel_attrs(root, (Node *)
513 prunequal,
514 nappinfos,
515 appinfos);
517 pfree(appinfos);
520 partprunequal = prunequal;
522 else
525 * For sub-partitioned tables the columns may not be in the same
526 * order as the parent, so we must translate the prunequal to make
527 * it compatible with this relation.
529 partprunequal = (List *)
530 adjust_appendrel_attrs_multilevel(root,
531 (Node *) prunequal,
532 subpart,
533 targetpart);
537 * Convert pruning qual to pruning steps. We may need to do this
538 * twice, once to obtain executor startup pruning steps, and once for
539 * executor per-scan pruning steps. This first pass creates startup
540 * pruning steps and detects whether there's any possibly-useful quals
541 * that would require per-scan pruning.
543 gen_partprune_steps(subpart, partprunequal, PARTTARGET_INITIAL,
544 &context);
546 if (context.contradictory)
549 * This shouldn't happen as the planner should have detected this
550 * earlier. However, we do use additional quals from parameterized
551 * paths here. These do only compare Params to the partition key,
552 * so this shouldn't cause the discovery of any new qual
553 * contradictions that were not previously discovered as the Param
554 * values are unknown during planning. Anyway, we'd better do
555 * something sane here, so let's just disable run-time pruning.
557 return NIL;
561 * If no mutable operators or expressions appear in usable pruning
562 * clauses, then there's no point in running startup pruning, because
563 * plan-time pruning should have pruned everything prunable.
565 if (context.has_mutable_op || context.has_mutable_arg)
566 initial_pruning_steps = context.steps;
567 else
568 initial_pruning_steps = NIL;
571 * If no exec Params appear in potentially-usable pruning clauses,
572 * then there's no point in even thinking about per-scan pruning.
574 if (context.has_exec_param)
576 /* ... OK, we'd better think about it */
577 gen_partprune_steps(subpart, partprunequal, PARTTARGET_EXEC,
578 &context);
580 if (context.contradictory)
582 /* As above, skip run-time pruning if anything fishy happens */
583 return NIL;
586 exec_pruning_steps = context.steps;
589 * Detect which exec Params actually got used; the fact that some
590 * were in available clauses doesn't mean we actually used them.
591 * Skip per-scan pruning if there are none.
593 execparamids = get_partkey_exec_paramids(exec_pruning_steps);
595 if (bms_is_empty(execparamids))
596 exec_pruning_steps = NIL;
598 else
600 /* No exec Params anywhere, so forget about scan-time pruning */
601 exec_pruning_steps = NIL;
602 execparamids = NULL;
605 if (initial_pruning_steps || exec_pruning_steps)
606 doruntimeprune = true;
608 /* Begin constructing the PartitionedRelPruneInfo for this rel */
609 pinfo = makeNode(PartitionedRelPruneInfo);
610 pinfo->rtindex = rti;
611 pinfo->initial_pruning_steps = initial_pruning_steps;
612 pinfo->exec_pruning_steps = exec_pruning_steps;
613 pinfo->execparamids = execparamids;
614 /* Remaining fields will be filled in the next loop */
616 pinfolist = lappend(pinfolist, pinfo);
619 if (!doruntimeprune)
621 /* No run-time pruning required. */
622 pfree(relid_subpart_map);
623 return NIL;
627 * Run-time pruning will be required, so initialize other information.
628 * That includes two maps -- one needed to convert partition indexes of
629 * leaf partitions to the indexes of their subplans in the subplan list,
630 * another needed to convert partition indexes of sub-partitioned
631 * partitions to the indexes of their PartitionedRelPruneInfo in the
632 * PartitionedRelPruneInfo list.
634 foreach(lc, pinfolist)
636 PartitionedRelPruneInfo *pinfo = lfirst(lc);
637 RelOptInfo *subpart = find_base_rel(root, pinfo->rtindex);
638 Bitmapset *present_parts;
639 int nparts = subpart->nparts;
640 int *subplan_map;
641 int *subpart_map;
642 Oid *relid_map;
645 * Construct the subplan and subpart maps for this partitioning level.
646 * Here we convert to zero-based indexes, with -1 for empty entries.
647 * Also construct a Bitmapset of all partitions that are present (that
648 * is, not pruned already).
650 subplan_map = (int *) palloc(nparts * sizeof(int));
651 memset(subplan_map, -1, nparts * sizeof(int));
652 subpart_map = (int *) palloc(nparts * sizeof(int));
653 memset(subpart_map, -1, nparts * sizeof(int));
654 relid_map = (Oid *) palloc0(nparts * sizeof(Oid));
655 present_parts = NULL;
657 i = -1;
658 while ((i = bms_next_member(subpart->live_parts, i)) >= 0)
660 RelOptInfo *partrel = subpart->part_rels[i];
661 int subplanidx;
662 int subpartidx;
664 Assert(partrel != NULL);
666 subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;
667 subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
668 relid_map[i] = planner_rt_fetch(partrel->relid, root)->relid;
669 if (subplanidx >= 0)
671 present_parts = bms_add_member(present_parts, i);
673 /* Record finding this subplan */
674 subplansfound = bms_add_member(subplansfound, subplanidx);
676 else if (subpartidx >= 0)
677 present_parts = bms_add_member(present_parts, i);
681 * Ensure there were no stray PartitionedRelPruneInfo generated for
682 * partitioned tables that we have no sub-paths or
683 * sub-PartitionedRelPruneInfo for.
685 Assert(!bms_is_empty(present_parts));
687 /* Record the maps and other information. */
688 pinfo->present_parts = present_parts;
689 pinfo->nparts = nparts;
690 pinfo->subplan_map = subplan_map;
691 pinfo->subpart_map = subpart_map;
692 pinfo->relid_map = relid_map;
695 pfree(relid_subpart_map);
697 *matchedsubplans = subplansfound;
699 return pinfolist;
703 * gen_partprune_steps
704 * Process 'clauses' (typically a rel's baserestrictinfo list of clauses)
705 * and create a list of "partition pruning steps".
707 * 'target' tells whether to generate pruning steps for planning (use
708 * immutable clauses only), or for executor startup (use any allowable
709 * clause except ones containing PARAM_EXEC Params), or for executor
710 * per-scan pruning (use any allowable clause).
712 * 'context' is an output argument that receives the steps list as well as
713 * some subsidiary flags; see the GeneratePruningStepsContext typedef.
715 static void
716 gen_partprune_steps(RelOptInfo *rel, List *clauses, PartClauseTarget target,
717 GeneratePruningStepsContext *context)
719 /* Initialize all output values to zero/false/NULL */
720 memset(context, 0, sizeof(GeneratePruningStepsContext));
721 context->rel = rel;
722 context->target = target;
725 * If this partitioned table is in turn a partition, and it shares any
726 * partition keys with its parent, then it's possible that the hierarchy
727 * allows the parent a narrower range of values than some of its
728 * partitions (particularly the default one). This is normally not
729 * useful, but it can be to prune the default partition.
731 if (partition_bound_has_default(rel->boundinfo) && rel->partition_qual)
733 /* Make a copy to avoid modifying the passed-in List */
734 clauses = list_concat_copy(clauses, rel->partition_qual);
737 /* Down into the rabbit-hole. */
738 (void) gen_partprune_steps_internal(context, clauses);
742 * prune_append_rel_partitions
743 * Process rel's baserestrictinfo and make use of quals which can be
744 * evaluated during query planning in order to determine the minimum set
745 * of partitions which must be scanned to satisfy these quals. Returns
746 * the matching partitions in the form of a Bitmapset containing the
747 * partitions' indexes in the rel's part_rels array.
749 * Callers must ensure that 'rel' is a partitioned table.
751 Bitmapset *
752 prune_append_rel_partitions(RelOptInfo *rel)
754 List *clauses = rel->baserestrictinfo;
755 List *pruning_steps;
756 GeneratePruningStepsContext gcontext;
757 PartitionPruneContext context;
759 Assert(rel->part_scheme != NULL);
761 /* If there are no partitions, return the empty set */
762 if (rel->nparts == 0)
763 return NULL;
766 * If pruning is disabled or if there are no clauses to prune with, return
767 * all partitions.
769 if (!enable_partition_pruning || clauses == NIL)
770 return bms_add_range(NULL, 0, rel->nparts - 1);
773 * Process clauses to extract pruning steps that are usable at plan time.
774 * If the clauses are found to be contradictory, we can return the empty
775 * set.
777 gen_partprune_steps(rel, clauses, PARTTARGET_PLANNER,
778 &gcontext);
779 if (gcontext.contradictory)
780 return NULL;
781 pruning_steps = gcontext.steps;
783 /* If there's nothing usable, return all partitions */
784 if (pruning_steps == NIL)
785 return bms_add_range(NULL, 0, rel->nparts - 1);
787 /* Set up PartitionPruneContext */
788 context.strategy = rel->part_scheme->strategy;
789 context.partnatts = rel->part_scheme->partnatts;
790 context.nparts = rel->nparts;
791 context.boundinfo = rel->boundinfo;
792 context.partcollation = rel->part_scheme->partcollation;
793 context.partsupfunc = rel->part_scheme->partsupfunc;
794 context.stepcmpfuncs = (FmgrInfo *) palloc0(sizeof(FmgrInfo) *
795 context.partnatts *
796 list_length(pruning_steps));
797 context.ppccontext = CurrentMemoryContext;
799 /* These are not valid when being called from the planner */
800 context.planstate = NULL;
801 context.exprcontext = NULL;
802 context.exprstates = NULL;
804 /* Actual pruning happens here. */
805 return get_matching_partitions(&context, pruning_steps);
809 * get_matching_partitions
810 * Determine partitions that survive partition pruning
812 * Note: context->exprcontext must be valid when the pruning_steps were
813 * generated with a target other than PARTTARGET_PLANNER.
815 * Returns a Bitmapset of the RelOptInfo->part_rels indexes of the surviving
816 * partitions.
818 Bitmapset *
819 get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
821 Bitmapset *result;
822 int num_steps = list_length(pruning_steps),
824 PruneStepResult **results,
825 *final_result;
826 ListCell *lc;
827 bool scan_default;
829 /* If there are no pruning steps then all partitions match. */
830 if (num_steps == 0)
832 Assert(context->nparts > 0);
833 return bms_add_range(NULL, 0, context->nparts - 1);
837 * Allocate space for individual pruning steps to store its result. Each
838 * slot will hold a PruneStepResult after performing a given pruning step.
839 * Later steps may use the result of one or more earlier steps. The
840 * result of applying all pruning steps is the value contained in the slot
841 * of the last pruning step.
843 results = (PruneStepResult **)
844 palloc0(num_steps * sizeof(PruneStepResult *));
845 foreach(lc, pruning_steps)
847 PartitionPruneStep *step = lfirst(lc);
849 switch (nodeTag(step))
851 case T_PartitionPruneStepOp:
852 results[step->step_id] =
853 perform_pruning_base_step(context,
854 (PartitionPruneStepOp *) step);
855 break;
857 case T_PartitionPruneStepCombine:
858 results[step->step_id] =
859 perform_pruning_combine_step(context,
860 (PartitionPruneStepCombine *) step,
861 results);
862 break;
864 default:
865 elog(ERROR, "invalid pruning step type: %d",
866 (int) nodeTag(step));
871 * At this point we know the offsets of all the datums whose corresponding
872 * partitions need to be in the result, including special null-accepting
873 * and default partitions. Collect the actual partition indexes now.
875 final_result = results[num_steps - 1];
876 Assert(final_result != NULL);
877 i = -1;
878 result = NULL;
879 scan_default = final_result->scan_default;
880 while ((i = bms_next_member(final_result->bound_offsets, i)) >= 0)
882 int partindex;
884 Assert(i < context->boundinfo->nindexes);
885 partindex = context->boundinfo->indexes[i];
887 if (partindex < 0)
890 * In range partitioning cases, if a partition index is -1 it
891 * means that the bound at the offset is the upper bound for a
892 * range not covered by any partition (other than a possible
893 * default partition). In hash partitioning, the same means no
894 * partition has been defined for the corresponding remainder
895 * value.
897 * In either case, the value is still part of the queried range of
898 * values, so mark to scan the default partition if one exists.
900 scan_default |= partition_bound_has_default(context->boundinfo);
901 continue;
904 result = bms_add_member(result, partindex);
907 /* Add the null and/or default partition if needed and present. */
908 if (final_result->scan_null)
910 Assert(context->strategy == PARTITION_STRATEGY_LIST);
911 Assert(partition_bound_accepts_nulls(context->boundinfo));
912 result = bms_add_member(result, context->boundinfo->null_index);
914 if (scan_default)
916 Assert(context->strategy == PARTITION_STRATEGY_LIST ||
917 context->strategy == PARTITION_STRATEGY_RANGE);
918 Assert(partition_bound_has_default(context->boundinfo));
919 result = bms_add_member(result, context->boundinfo->default_index);
922 return result;
926 * gen_partprune_steps_internal
927 * Processes 'clauses' to generate a List of partition pruning steps. We
928 * return NIL when no steps were generated.
930 * These partition pruning steps come in 2 forms; operator steps and combine
931 * steps.
933 * Operator steps (PartitionPruneStepOp) contain details of clauses that we
934 * determined that we can use for partition pruning. These contain details of
935 * the expression which is being compared to the partition key and the
936 * comparison function.
938 * Combine steps (PartitionPruneStepCombine) instruct the partition pruning
939 * code how it should produce a single set of partitions from multiple input
940 * operator and other combine steps. A PARTPRUNE_COMBINE_INTERSECT type
941 * combine step will merge its input steps to produce a result which only
942 * contains the partitions which are present in all of the input operator
943 * steps. A PARTPRUNE_COMBINE_UNION combine step will produce a result that
944 * has all of the partitions from each of the input operator steps.
946 * For BoolExpr clauses, each argument is processed recursively. Steps
947 * generated from processing an OR BoolExpr will be combined using
948 * PARTPRUNE_COMBINE_UNION. AND BoolExprs get combined using
949 * PARTPRUNE_COMBINE_INTERSECT.
951 * Otherwise, the list of clauses we receive we assume to be mutually ANDed.
952 * We generate all of the pruning steps we can based on these clauses and then
953 * at the end, if we have more than 1 step, we combine each step with a
954 * PARTPRUNE_COMBINE_INTERSECT combine step. Single steps are returned as-is.
956 * If we find clauses that are mutually contradictory, or contradictory with
957 * the partitioning constraint, or a pseudoconstant clause that contains
958 * false, we set context->contradictory to true and return NIL (that is, no
959 * pruning steps). Caller should consider all partitions as pruned in that
960 * case.
962 static List *
963 gen_partprune_steps_internal(GeneratePruningStepsContext *context,
964 List *clauses)
966 PartitionScheme part_scheme = context->rel->part_scheme;
967 List *keyclauses[PARTITION_MAX_KEYS];
968 Bitmapset *nullkeys = NULL,
969 *notnullkeys = NULL;
970 bool generate_opsteps = false;
971 List *result = NIL;
972 ListCell *lc;
975 * If this partitioned relation has a default partition and is itself a
976 * partition (as evidenced by partition_qual being not NIL), we first
977 * check if the clauses contradict the partition constraint. If they do,
978 * there's no need to generate any steps as it'd already be proven that no
979 * partitions need to be scanned.
981 * This is a measure of last resort only to be used because the default
982 * partition cannot be pruned using the steps generated from clauses that
983 * contradict the parent's partition constraint; regular pruning, which is
984 * cheaper, is sufficient when no default partition exists.
986 if (partition_bound_has_default(context->rel->boundinfo) &&
987 predicate_refuted_by(context->rel->partition_qual, clauses, false))
989 context->contradictory = true;
990 return NIL;
993 memset(keyclauses, 0, sizeof(keyclauses));
994 foreach(lc, clauses)
996 Expr *clause = (Expr *) lfirst(lc);
997 int i;
999 /* Look through RestrictInfo, if any */
1000 if (IsA(clause, RestrictInfo))
1001 clause = ((RestrictInfo *) clause)->clause;
1003 /* Constant-false-or-null is contradictory */
1004 if (IsA(clause, Const) &&
1005 (((Const *) clause)->constisnull ||
1006 !DatumGetBool(((Const *) clause)->constvalue)))
1008 context->contradictory = true;
1009 return NIL;
1012 /* Get the BoolExpr's out of the way. */
1013 if (IsA(clause, BoolExpr))
1016 * Generate steps for arguments.
1018 * While steps generated for the arguments themselves will be
1019 * added to context->steps during recursion and will be evaluated
1020 * independently, collect their step IDs to be stored in the
1021 * combine step we'll be creating.
1023 if (is_orclause(clause))
1025 List *arg_stepids = NIL;
1026 bool all_args_contradictory = true;
1027 ListCell *lc1;
1030 * We can share the outer context area with the recursive
1031 * call, but contradictory had better not be true yet.
1033 Assert(!context->contradictory);
1036 * Get pruning step for each arg. If we get contradictory for
1037 * all args, it means the OR expression is false as a whole.
1039 foreach(lc1, ((BoolExpr *) clause)->args)
1041 Expr *arg = lfirst(lc1);
1042 bool arg_contradictory;
1043 List *argsteps;
1045 argsteps = gen_partprune_steps_internal(context,
1046 list_make1(arg));
1047 arg_contradictory = context->contradictory;
1048 /* Keep context->contradictory clear till we're done */
1049 context->contradictory = false;
1051 if (arg_contradictory)
1053 /* Just ignore self-contradictory arguments. */
1054 continue;
1056 else
1057 all_args_contradictory = false;
1059 if (argsteps != NIL)
1062 * gen_partprune_steps_internal() always adds a single
1063 * combine step when it generates multiple steps, so
1064 * here we can just pay attention to the last one in
1065 * the list. If it just generated one, then the last
1066 * one in the list is still the one we want.
1068 PartitionPruneStep *last = llast(argsteps);
1070 arg_stepids = lappend_int(arg_stepids, last->step_id);
1072 else
1074 PartitionPruneStep *orstep;
1077 * The arg didn't contain a clause matching this
1078 * partition key. We cannot prune using such an arg.
1079 * To indicate that to the pruning code, we must
1080 * construct a dummy PartitionPruneStepCombine whose
1081 * source_stepids is set to an empty List.
1083 orstep = gen_prune_step_combine(context, NIL,
1084 PARTPRUNE_COMBINE_UNION);
1085 arg_stepids = lappend_int(arg_stepids, orstep->step_id);
1089 /* If all the OR arms are contradictory, we can stop */
1090 if (all_args_contradictory)
1092 context->contradictory = true;
1093 return NIL;
1096 if (arg_stepids != NIL)
1098 PartitionPruneStep *step;
1100 step = gen_prune_step_combine(context, arg_stepids,
1101 PARTPRUNE_COMBINE_UNION);
1102 result = lappend(result, step);
1104 continue;
1106 else if (is_andclause(clause))
1108 List *args = ((BoolExpr *) clause)->args;
1109 List *argsteps;
1112 * args may itself contain clauses of arbitrary type, so just
1113 * recurse and later combine the component partitions sets
1114 * using a combine step.
1116 argsteps = gen_partprune_steps_internal(context, args);
1118 /* If any AND arm is contradictory, we can stop immediately */
1119 if (context->contradictory)
1120 return NIL;
1123 * gen_partprune_steps_internal() always adds a single combine
1124 * step when it generates multiple steps, so here we can just
1125 * pay attention to the last one in the list. If it just
1126 * generated one, then the last one in the list is still the
1127 * one we want.
1129 if (argsteps != NIL)
1130 result = lappend(result, llast(argsteps));
1132 continue;
1136 * Fall-through for a NOT clause, which if it's a Boolean clause,
1137 * will be handled in match_clause_to_partition_key(). We
1138 * currently don't perform any pruning for more complex NOT
1139 * clauses.
1144 * See if we can match this clause to any of the partition keys.
1146 for (i = 0; i < part_scheme->partnatts; i++)
1148 Expr *partkey = linitial(context->rel->partexprs[i]);
1149 bool clause_is_not_null = false;
1150 PartClauseInfo *pc = NULL;
1151 List *clause_steps = NIL;
1153 switch (match_clause_to_partition_key(context,
1154 clause, partkey, i,
1155 &clause_is_not_null,
1156 &pc, &clause_steps))
1158 case PARTCLAUSE_MATCH_CLAUSE:
1159 Assert(pc != NULL);
1162 * Since we only allow strict operators, check for any
1163 * contradicting IS NULL.
1165 if (bms_is_member(i, nullkeys))
1167 context->contradictory = true;
1168 return NIL;
1170 generate_opsteps = true;
1171 keyclauses[i] = lappend(keyclauses[i], pc);
1172 break;
1174 case PARTCLAUSE_MATCH_NULLNESS:
1175 if (!clause_is_not_null)
1178 * check for conflicting IS NOT NULL as well as
1179 * contradicting strict clauses
1181 if (bms_is_member(i, notnullkeys) ||
1182 keyclauses[i] != NIL)
1184 context->contradictory = true;
1185 return NIL;
1187 nullkeys = bms_add_member(nullkeys, i);
1189 else
1191 /* check for conflicting IS NULL */
1192 if (bms_is_member(i, nullkeys))
1194 context->contradictory = true;
1195 return NIL;
1197 notnullkeys = bms_add_member(notnullkeys, i);
1199 break;
1201 case PARTCLAUSE_MATCH_STEPS:
1202 Assert(clause_steps != NIL);
1203 result = list_concat(result, clause_steps);
1204 break;
1206 case PARTCLAUSE_MATCH_CONTRADICT:
1207 /* We've nothing more to do if a contradiction was found. */
1208 context->contradictory = true;
1209 return NIL;
1211 case PARTCLAUSE_NOMATCH:
1214 * Clause didn't match this key, but it might match the
1215 * next one.
1217 continue;
1219 case PARTCLAUSE_UNSUPPORTED:
1220 /* This clause cannot be used for pruning. */
1221 break;
1224 /* done; go check the next clause. */
1225 break;
1229 /*-----------
1230 * Now generate some (more) pruning steps. We have three strategies:
1232 * 1) Generate pruning steps based on IS NULL clauses:
1233 * a) For list partitioning, null partition keys can only be found in
1234 * the designated null-accepting partition, so if there are IS NULL
1235 * clauses containing partition keys we should generate a pruning
1236 * step that gets rid of all partitions but that one. We can
1237 * disregard any OpExpr we may have found.
1238 * b) For range partitioning, only the default partition can contain
1239 * NULL values, so the same rationale applies.
1240 * c) For hash partitioning, we only apply this strategy if we have
1241 * IS NULL clauses for all the keys. Strategy 2 below will take
1242 * care of the case where some keys have OpExprs and others have
1243 * IS NULL clauses.
1245 * 2) If not, generate steps based on OpExprs we have (if any).
1247 * 3) If this doesn't work either, we may be able to generate steps to
1248 * prune just the null-accepting partition (if one exists), if we have
1249 * IS NOT NULL clauses for all partition keys.
1251 if (!bms_is_empty(nullkeys) &&
1252 (part_scheme->strategy == PARTITION_STRATEGY_LIST ||
1253 part_scheme->strategy == PARTITION_STRATEGY_RANGE ||
1254 (part_scheme->strategy == PARTITION_STRATEGY_HASH &&
1255 bms_num_members(nullkeys) == part_scheme->partnatts)))
1257 PartitionPruneStep *step;
1259 /* Strategy 1 */
1260 step = gen_prune_step_op(context, InvalidStrategy,
1261 false, NIL, NIL, nullkeys);
1262 result = lappend(result, step);
1264 else if (generate_opsteps)
1266 List *opsteps;
1268 /* Strategy 2 */
1269 opsteps = gen_prune_steps_from_opexps(context, keyclauses, nullkeys);
1270 result = list_concat(result, opsteps);
1272 else if (bms_num_members(notnullkeys) == part_scheme->partnatts)
1274 PartitionPruneStep *step;
1276 /* Strategy 3 */
1277 step = gen_prune_step_op(context, InvalidStrategy,
1278 false, NIL, NIL, NULL);
1279 result = lappend(result, step);
1283 * Finally, if there are multiple steps, since the 'clauses' are mutually
1284 * ANDed, add an INTERSECT step to combine the partition sets resulting
1285 * from them and append it to the result list.
1287 if (list_length(result) > 1)
1289 List *step_ids = NIL;
1290 PartitionPruneStep *final;
1292 foreach(lc, result)
1294 PartitionPruneStep *step = lfirst(lc);
1296 step_ids = lappend_int(step_ids, step->step_id);
1299 final = gen_prune_step_combine(context, step_ids,
1300 PARTPRUNE_COMBINE_INTERSECT);
1301 result = lappend(result, final);
1304 return result;
1308 * gen_prune_step_op
1309 * Generate a pruning step for a specific operator
1311 * The step is assigned a unique step identifier and added to context's 'steps'
1312 * list.
1314 static PartitionPruneStep *
1315 gen_prune_step_op(GeneratePruningStepsContext *context,
1316 StrategyNumber opstrategy, bool op_is_ne,
1317 List *exprs, List *cmpfns,
1318 Bitmapset *nullkeys)
1320 PartitionPruneStepOp *opstep = makeNode(PartitionPruneStepOp);
1322 opstep->step.step_id = context->next_step_id++;
1325 * For clauses that contain an <> operator, set opstrategy to
1326 * InvalidStrategy to signal get_matching_list_bounds to do the right
1327 * thing.
1329 opstep->opstrategy = op_is_ne ? InvalidStrategy : opstrategy;
1330 Assert(list_length(exprs) == list_length(cmpfns));
1331 opstep->exprs = exprs;
1332 opstep->cmpfns = cmpfns;
1333 opstep->nullkeys = nullkeys;
1335 context->steps = lappend(context->steps, opstep);
1337 return (PartitionPruneStep *) opstep;
1341 * gen_prune_step_combine
1342 * Generate a pruning step for a combination of several other steps
1344 * The step is assigned a unique step identifier and added to context's
1345 * 'steps' list.
1347 static PartitionPruneStep *
1348 gen_prune_step_combine(GeneratePruningStepsContext *context,
1349 List *source_stepids,
1350 PartitionPruneCombineOp combineOp)
1352 PartitionPruneStepCombine *cstep = makeNode(PartitionPruneStepCombine);
1354 cstep->step.step_id = context->next_step_id++;
1355 cstep->combineOp = combineOp;
1356 cstep->source_stepids = source_stepids;
1358 context->steps = lappend(context->steps, cstep);
1360 return (PartitionPruneStep *) cstep;
1364 * gen_prune_steps_from_opexps
1365 * Generate and return a list of PartitionPruneStepOp that are based on
1366 * OpExpr and BooleanTest clauses that have been matched to the partition
1367 * key.
1369 * 'keyclauses' is an array of List pointers, indexed by the partition key's
1370 * index. Each List element in the array can contain clauses that match to
1371 * the corresponding partition key column. Partition key columns without any
1372 * matched clauses will have an empty List.
1374 * Some partitioning strategies allow pruning to still occur when we only have
1375 * clauses for a prefix of the partition key columns, for example, RANGE
1376 * partitioning. Other strategies, such as HASH partitioning, require clauses
1377 * for all partition key columns.
1379 * When we return multiple pruning steps here, it's up to the caller to add a
1380 * relevant "combine" step to combine the returned steps. This is not done
1381 * here as callers may wish to include additional pruning steps before
1382 * combining them all.
1384 static List *
1385 gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
1386 List **keyclauses, Bitmapset *nullkeys)
1388 PartitionScheme part_scheme = context->rel->part_scheme;
1389 List *opsteps = NIL;
1390 List *btree_clauses[BTMaxStrategyNumber + 1],
1391 *hash_clauses[HTMaxStrategyNumber + 1];
1392 int i;
1393 ListCell *lc;
1395 memset(btree_clauses, 0, sizeof(btree_clauses));
1396 memset(hash_clauses, 0, sizeof(hash_clauses));
1397 for (i = 0; i < part_scheme->partnatts; i++)
1399 List *clauselist = keyclauses[i];
1400 bool consider_next_key = true;
1403 * For range partitioning, if we have no clauses for the current key,
1404 * we can't consider any later keys either, so we can stop here.
1406 if (part_scheme->strategy == PARTITION_STRATEGY_RANGE &&
1407 clauselist == NIL)
1408 break;
1411 * For hash partitioning, if a column doesn't have the necessary
1412 * equality clause, there should be an IS NULL clause, otherwise
1413 * pruning is not possible.
1415 if (part_scheme->strategy == PARTITION_STRATEGY_HASH &&
1416 clauselist == NIL && !bms_is_member(i, nullkeys))
1417 return NIL;
1419 foreach(lc, clauselist)
1421 PartClauseInfo *pc = (PartClauseInfo *) lfirst(lc);
1422 Oid lefttype,
1423 righttype;
1425 /* Look up the operator's btree/hash strategy number. */
1426 if (pc->op_strategy == InvalidStrategy)
1427 get_op_opfamily_properties(pc->opno,
1428 part_scheme->partopfamily[i],
1429 false,
1430 &pc->op_strategy,
1431 &lefttype,
1432 &righttype);
1434 switch (part_scheme->strategy)
1436 case PARTITION_STRATEGY_LIST:
1437 case PARTITION_STRATEGY_RANGE:
1438 btree_clauses[pc->op_strategy] =
1439 lappend(btree_clauses[pc->op_strategy], pc);
1442 * We can't consider subsequent partition keys if the
1443 * clause for the current key contains a non-inclusive
1444 * operator.
1446 if (pc->op_strategy == BTLessStrategyNumber ||
1447 pc->op_strategy == BTGreaterStrategyNumber)
1448 consider_next_key = false;
1449 break;
1451 case PARTITION_STRATEGY_HASH:
1452 if (pc->op_strategy != HTEqualStrategyNumber)
1453 elog(ERROR, "invalid clause for hash partitioning");
1454 hash_clauses[pc->op_strategy] =
1455 lappend(hash_clauses[pc->op_strategy], pc);
1456 break;
1458 default:
1459 elog(ERROR, "invalid partition strategy: %c",
1460 part_scheme->strategy);
1461 break;
1466 * If we've decided that clauses for subsequent partition keys
1467 * wouldn't be useful for pruning, don't search any further.
1469 if (!consider_next_key)
1470 break;
1474 * Now, we have divided clauses according to their operator strategies.
1475 * Check for each strategy if we can generate pruning step(s) by
1476 * collecting a list of expressions whose values will constitute a vector
1477 * that can be used as a lookup key by a partition bound searching
1478 * function.
1480 switch (part_scheme->strategy)
1482 case PARTITION_STRATEGY_LIST:
1483 case PARTITION_STRATEGY_RANGE:
1485 List *eq_clauses = btree_clauses[BTEqualStrategyNumber];
1486 List *le_clauses = btree_clauses[BTLessEqualStrategyNumber];
1487 List *ge_clauses = btree_clauses[BTGreaterEqualStrategyNumber];
1488 int strat;
1491 * For each clause under consideration for a given strategy,
1492 * we collect expressions from clauses for earlier keys, whose
1493 * operator strategy is inclusive, into a list called
1494 * 'prefix'. By appending the clause's own expression to the
1495 * 'prefix', we'll generate one step using the so generated
1496 * vector and assign the current strategy to it. Actually,
1497 * 'prefix' might contain multiple clauses for the same key,
1498 * in which case, we must generate steps for various
1499 * combinations of expressions of different keys, which
1500 * get_steps_using_prefix takes care of for us.
1502 for (strat = 1; strat <= BTMaxStrategyNumber; strat++)
1504 foreach(lc, btree_clauses[strat])
1506 PartClauseInfo *pc = lfirst(lc);
1507 ListCell *eq_start;
1508 ListCell *le_start;
1509 ListCell *ge_start;
1510 ListCell *lc1;
1511 List *prefix = NIL;
1512 List *pc_steps;
1513 bool prefix_valid = true;
1514 bool pk_has_clauses;
1515 int keyno;
1518 * If this is a clause for the first partition key,
1519 * there are no preceding expressions; generate a
1520 * pruning step without a prefix.
1522 * Note that we pass NULL for step_nullkeys, because
1523 * we don't search list/range partition bounds where
1524 * some keys are NULL.
1526 if (pc->keyno == 0)
1528 Assert(pc->op_strategy == strat);
1529 pc_steps = get_steps_using_prefix(context, strat,
1530 pc->op_is_ne,
1531 pc->expr,
1532 pc->cmpfn,
1534 NULL,
1535 NIL);
1536 opsteps = list_concat(opsteps, pc_steps);
1537 continue;
1540 eq_start = list_head(eq_clauses);
1541 le_start = list_head(le_clauses);
1542 ge_start = list_head(ge_clauses);
1545 * We arrange clauses into prefix in ascending order
1546 * of their partition key numbers.
1548 for (keyno = 0; keyno < pc->keyno; keyno++)
1550 pk_has_clauses = false;
1553 * Expressions from = clauses can always be in the
1554 * prefix, provided they're from an earlier key.
1556 for_each_cell(lc1, eq_clauses, eq_start)
1558 PartClauseInfo *eqpc = lfirst(lc1);
1560 if (eqpc->keyno == keyno)
1562 prefix = lappend(prefix, eqpc);
1563 pk_has_clauses = true;
1565 else
1567 Assert(eqpc->keyno > keyno);
1568 break;
1571 eq_start = lc1;
1574 * If we're generating steps for </<= strategy, we
1575 * can add other <= clauses to the prefix,
1576 * provided they're from an earlier key.
1578 if (strat == BTLessStrategyNumber ||
1579 strat == BTLessEqualStrategyNumber)
1581 for_each_cell(lc1, le_clauses, le_start)
1583 PartClauseInfo *lepc = lfirst(lc1);
1585 if (lepc->keyno == keyno)
1587 prefix = lappend(prefix, lepc);
1588 pk_has_clauses = true;
1590 else
1592 Assert(lepc->keyno > keyno);
1593 break;
1596 le_start = lc1;
1600 * If we're generating steps for >/>= strategy, we
1601 * can add other >= clauses to the prefix,
1602 * provided they're from an earlier key.
1604 if (strat == BTGreaterStrategyNumber ||
1605 strat == BTGreaterEqualStrategyNumber)
1607 for_each_cell(lc1, ge_clauses, ge_start)
1609 PartClauseInfo *gepc = lfirst(lc1);
1611 if (gepc->keyno == keyno)
1613 prefix = lappend(prefix, gepc);
1614 pk_has_clauses = true;
1616 else
1618 Assert(gepc->keyno > keyno);
1619 break;
1622 ge_start = lc1;
1626 * If this key has no clauses, prefix is not valid
1627 * anymore.
1629 if (!pk_has_clauses)
1631 prefix_valid = false;
1632 break;
1637 * If prefix_valid, generate PartitionPruneStepOps.
1638 * Otherwise, we would not find clauses for a valid
1639 * subset of the partition keys anymore for the
1640 * strategy; give up on generating partition pruning
1641 * steps further for the strategy.
1643 * As mentioned above, if 'prefix' contains multiple
1644 * expressions for the same key, the following will
1645 * generate multiple steps, one for each combination
1646 * of the expressions for different keys.
1648 * Note that we pass NULL for step_nullkeys, because
1649 * we don't search list/range partition bounds where
1650 * some keys are NULL.
1652 if (prefix_valid)
1654 Assert(pc->op_strategy == strat);
1655 pc_steps = get_steps_using_prefix(context, strat,
1656 pc->op_is_ne,
1657 pc->expr,
1658 pc->cmpfn,
1659 pc->keyno,
1660 NULL,
1661 prefix);
1662 opsteps = list_concat(opsteps, pc_steps);
1664 else
1665 break;
1668 break;
1671 case PARTITION_STRATEGY_HASH:
1673 List *eq_clauses = hash_clauses[HTEqualStrategyNumber];
1675 /* For hash partitioning, we have just the = strategy. */
1676 if (eq_clauses != NIL)
1678 PartClauseInfo *pc;
1679 List *pc_steps;
1680 List *prefix = NIL;
1681 int last_keyno;
1682 ListCell *lc1;
1685 * Locate the clause for the greatest column. This may
1686 * not belong to the last partition key, but it is the
1687 * clause belonging to the last partition key we found a
1688 * clause for above.
1690 pc = llast(eq_clauses);
1693 * There might be multiple clauses which matched to that
1694 * partition key; find the first such clause. While at
1695 * it, add all the clauses before that one to 'prefix'.
1697 last_keyno = pc->keyno;
1698 foreach(lc, eq_clauses)
1700 pc = lfirst(lc);
1701 if (pc->keyno == last_keyno)
1702 break;
1703 prefix = lappend(prefix, pc);
1707 * For each clause for the "last" column, after appending
1708 * the clause's own expression to the 'prefix', we'll
1709 * generate one step using the so generated vector and
1710 * assign = as its strategy. Actually, 'prefix' might
1711 * contain multiple clauses for the same key, in which
1712 * case, we must generate steps for various combinations
1713 * of expressions of different keys, which
1714 * get_steps_using_prefix will take care of for us.
1716 for_each_cell(lc1, eq_clauses, lc)
1718 pc = lfirst(lc1);
1721 * Note that we pass nullkeys for step_nullkeys,
1722 * because we need to tell hash partition bound search
1723 * function which of the keys we found IS NULL clauses
1724 * for.
1726 Assert(pc->op_strategy == HTEqualStrategyNumber);
1727 pc_steps =
1728 get_steps_using_prefix(context,
1729 HTEqualStrategyNumber,
1730 false,
1731 pc->expr,
1732 pc->cmpfn,
1733 pc->keyno,
1734 nullkeys,
1735 prefix);
1736 opsteps = list_concat(opsteps, pc_steps);
1739 break;
1742 default:
1743 elog(ERROR, "invalid partition strategy: %c",
1744 part_scheme->strategy);
1745 break;
1748 return opsteps;
1752 * If the partition key has a collation, then the clause must have the same
1753 * input collation. If the partition key is non-collatable, we assume the
1754 * collation doesn't matter, because while collation wasn't considered when
1755 * performing partitioning, the clause still may have a collation assigned
1756 * due to the other input being of a collatable type.
1758 * See also IndexCollMatchesExprColl.
1760 #define PartCollMatchesExprColl(partcoll, exprcoll) \
1761 ((partcoll) == InvalidOid || (partcoll) == (exprcoll))
1764 * match_clause_to_partition_key
1765 * Attempt to match the given 'clause' with the specified partition key.
1767 * Return value is:
1768 * * PARTCLAUSE_NOMATCH if the clause doesn't match this partition key (but
1769 * caller should keep trying, because it might match a subsequent key).
1770 * Output arguments: none set.
1772 * * PARTCLAUSE_MATCH_CLAUSE if there is a match.
1773 * Output arguments: *pc is set to a PartClauseInfo constructed for the
1774 * matched clause.
1776 * * PARTCLAUSE_MATCH_NULLNESS if there is a match, and the matched clause was
1777 * either a "a IS NULL" or "a IS NOT NULL" clause.
1778 * Output arguments: *clause_is_not_null is set to false in the former case
1779 * true otherwise.
1781 * * PARTCLAUSE_MATCH_STEPS if there is a match.
1782 * Output arguments: *clause_steps is set to the list of recursively
1783 * generated steps for the clause.
1785 * * PARTCLAUSE_MATCH_CONTRADICT if the clause is self-contradictory, ie
1786 * it provably returns FALSE or NULL.
1787 * Output arguments: none set.
1789 * * PARTCLAUSE_UNSUPPORTED if the clause doesn't match this partition key
1790 * and couldn't possibly match any other one either, due to its form or
1791 * properties (such as containing a volatile function).
1792 * Output arguments: none set.
1794 static PartClauseMatchStatus
1795 match_clause_to_partition_key(GeneratePruningStepsContext *context,
1796 Expr *clause, Expr *partkey, int partkeyidx,
1797 bool *clause_is_not_null, PartClauseInfo **pc,
1798 List **clause_steps)
1800 PartClauseMatchStatus boolmatchstatus;
1801 PartitionScheme part_scheme = context->rel->part_scheme;
1802 Oid partopfamily = part_scheme->partopfamily[partkeyidx],
1803 partcoll = part_scheme->partcollation[partkeyidx];
1804 Expr *expr;
1807 * Recognize specially shaped clauses that match a Boolean partition key.
1809 boolmatchstatus = match_boolean_partition_clause(partopfamily, clause,
1810 partkey, &expr);
1812 if (boolmatchstatus == PARTCLAUSE_MATCH_CLAUSE)
1814 PartClauseInfo *partclause;
1816 partclause = (PartClauseInfo *) palloc(sizeof(PartClauseInfo));
1817 partclause->keyno = partkeyidx;
1818 /* Do pruning with the Boolean equality operator. */
1819 partclause->opno = BooleanEqualOperator;
1820 partclause->op_is_ne = false;
1821 partclause->expr = expr;
1822 /* We know that expr is of Boolean type. */
1823 partclause->cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid;
1824 partclause->op_strategy = InvalidStrategy;
1826 *pc = partclause;
1828 return PARTCLAUSE_MATCH_CLAUSE;
1830 else if (IsA(clause, OpExpr) &&
1831 list_length(((OpExpr *) clause)->args) == 2)
1833 OpExpr *opclause = (OpExpr *) clause;
1834 Expr *leftop,
1835 *rightop;
1836 Oid opno,
1837 op_lefttype,
1838 op_righttype,
1839 negator = InvalidOid;
1840 Oid cmpfn;
1841 int op_strategy;
1842 bool is_opne_listp = false;
1843 PartClauseInfo *partclause;
1845 leftop = (Expr *) get_leftop(clause);
1846 if (IsA(leftop, RelabelType))
1847 leftop = ((RelabelType *) leftop)->arg;
1848 rightop = (Expr *) get_rightop(clause);
1849 if (IsA(rightop, RelabelType))
1850 rightop = ((RelabelType *) rightop)->arg;
1851 opno = opclause->opno;
1853 /* check if the clause matches this partition key */
1854 if (equal(leftop, partkey))
1855 expr = rightop;
1856 else if (equal(rightop, partkey))
1859 * It's only useful if we can commute the operator to put the
1860 * partkey on the left. If we can't, the clause can be deemed
1861 * UNSUPPORTED. Even if its leftop matches some later partkey, we
1862 * now know it has Vars on the right, so it's no use.
1864 opno = get_commutator(opno);
1865 if (!OidIsValid(opno))
1866 return PARTCLAUSE_UNSUPPORTED;
1867 expr = leftop;
1869 else
1870 /* clause does not match this partition key, but perhaps next. */
1871 return PARTCLAUSE_NOMATCH;
1874 * Partition key match also requires collation match. There may be
1875 * multiple partkeys with the same expression but different
1876 * collations, so failure is NOMATCH.
1878 if (!PartCollMatchesExprColl(partcoll, opclause->inputcollid))
1879 return PARTCLAUSE_NOMATCH;
1882 * See if the operator is relevant to the partitioning opfamily.
1884 * Normally we only care about operators that are listed as being part
1885 * of the partitioning operator family. But there is one exception:
1886 * the not-equals operators are not listed in any operator family
1887 * whatsoever, but their negators (equality) are. We can use one of
1888 * those if we find it, but only for list partitioning.
1890 * Note: we report NOMATCH on failure, in case a later partkey has the
1891 * same expression but different opfamily. That's unlikely, but not
1892 * much more so than duplicate expressions with different collations.
1894 if (op_in_opfamily(opno, partopfamily))
1896 get_op_opfamily_properties(opno, partopfamily, false,
1897 &op_strategy, &op_lefttype,
1898 &op_righttype);
1900 else
1902 if (part_scheme->strategy != PARTITION_STRATEGY_LIST)
1903 return PARTCLAUSE_NOMATCH;
1905 /* See if the negator is equality */
1906 negator = get_negator(opno);
1907 if (OidIsValid(negator) && op_in_opfamily(negator, partopfamily))
1909 get_op_opfamily_properties(negator, partopfamily, false,
1910 &op_strategy, &op_lefttype,
1911 &op_righttype);
1912 if (op_strategy == BTEqualStrategyNumber)
1913 is_opne_listp = true; /* bingo */
1916 /* Nope, it's not <> either. */
1917 if (!is_opne_listp)
1918 return PARTCLAUSE_NOMATCH;
1922 * Only allow strict operators. This will guarantee nulls are
1923 * filtered. (This test is likely useless, since btree and hash
1924 * comparison operators are generally strict.)
1926 if (!op_strict(opno))
1927 return PARTCLAUSE_UNSUPPORTED;
1930 * OK, we have a match to the partition key and a suitable operator.
1931 * Examine the other argument to see if it's usable for pruning.
1933 * In most of these cases, we can return UNSUPPORTED because the same
1934 * failure would occur no matter which partkey it's matched to. (In
1935 * particular, now that we've successfully matched one side of the
1936 * opclause to a partkey, there is no chance that matching the other
1937 * side to another partkey will produce a usable result, since that'd
1938 * mean there are Vars on both sides.)
1940 * Also, if we reject an argument for a target-dependent reason, set
1941 * appropriate fields of *context to report that. We postpone these
1942 * tests until after matching the partkey and the operator, so as to
1943 * reduce the odds of setting the context fields for clauses that do
1944 * not end up contributing to pruning steps.
1946 * First, check for non-Const argument. (We assume that any immutable
1947 * subexpression will have been folded to a Const already.)
1949 if (!IsA(expr, Const))
1951 Bitmapset *paramids;
1954 * When pruning in the planner, we only support pruning using
1955 * comparisons to constants. We cannot prune on the basis of
1956 * anything that's not immutable. (Note that has_mutable_arg and
1957 * has_exec_param do not get set for this target value.)
1959 if (context->target == PARTTARGET_PLANNER)
1960 return PARTCLAUSE_UNSUPPORTED;
1963 * We can never prune using an expression that contains Vars.
1965 if (contain_var_clause((Node *) expr))
1966 return PARTCLAUSE_UNSUPPORTED;
1969 * And we must reject anything containing a volatile function.
1970 * Stable functions are OK though.
1972 if (contain_volatile_functions((Node *) expr))
1973 return PARTCLAUSE_UNSUPPORTED;
1976 * See if there are any exec Params. If so, we can only use this
1977 * expression during per-scan pruning.
1979 paramids = pull_exec_paramids(expr);
1980 if (!bms_is_empty(paramids))
1982 context->has_exec_param = true;
1983 if (context->target != PARTTARGET_EXEC)
1984 return PARTCLAUSE_UNSUPPORTED;
1986 else
1988 /* It's potentially usable, but mutable */
1989 context->has_mutable_arg = true;
1994 * Check whether the comparison operator itself is immutable. (We
1995 * assume anything that's in a btree or hash opclass is at least
1996 * stable, but we need to check for immutability.)
1998 if (op_volatile(opno) != PROVOLATILE_IMMUTABLE)
2000 context->has_mutable_op = true;
2003 * When pruning in the planner, we cannot prune with mutable
2004 * operators.
2006 if (context->target == PARTTARGET_PLANNER)
2007 return PARTCLAUSE_UNSUPPORTED;
2011 * Now find the procedure to use, based on the types. If the clause's
2012 * other argument is of the same type as the partitioning opclass's
2013 * declared input type, we can use the procedure cached in
2014 * PartitionKey. If not, search for a cross-type one in the same
2015 * opfamily; if one doesn't exist, report no match.
2017 if (op_righttype == part_scheme->partopcintype[partkeyidx])
2018 cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid;
2019 else
2021 switch (part_scheme->strategy)
2024 * For range and list partitioning, we need the ordering
2025 * procedure with lefttype being the partition key's type,
2026 * and righttype the clause's operator's right type.
2028 case PARTITION_STRATEGY_LIST:
2029 case PARTITION_STRATEGY_RANGE:
2030 cmpfn =
2031 get_opfamily_proc(part_scheme->partopfamily[partkeyidx],
2032 part_scheme->partopcintype[partkeyidx],
2033 op_righttype, BTORDER_PROC);
2034 break;
2037 * For hash partitioning, we need the hashing procedure
2038 * for the clause's type.
2040 case PARTITION_STRATEGY_HASH:
2041 cmpfn =
2042 get_opfamily_proc(part_scheme->partopfamily[partkeyidx],
2043 op_righttype, op_righttype,
2044 HASHEXTENDED_PROC);
2045 break;
2047 default:
2048 elog(ERROR, "invalid partition strategy: %c",
2049 part_scheme->strategy);
2050 cmpfn = InvalidOid; /* keep compiler quiet */
2051 break;
2054 if (!OidIsValid(cmpfn))
2055 return PARTCLAUSE_NOMATCH;
2059 * Build the clause, passing the negator if applicable.
2061 partclause = (PartClauseInfo *) palloc(sizeof(PartClauseInfo));
2062 partclause->keyno = partkeyidx;
2063 if (is_opne_listp)
2065 Assert(OidIsValid(negator));
2066 partclause->opno = negator;
2067 partclause->op_is_ne = true;
2068 partclause->op_strategy = InvalidStrategy;
2070 else
2072 partclause->opno = opno;
2073 partclause->op_is_ne = false;
2074 partclause->op_strategy = op_strategy;
2076 partclause->expr = expr;
2077 partclause->cmpfn = cmpfn;
2079 *pc = partclause;
2081 return PARTCLAUSE_MATCH_CLAUSE;
2083 else if (IsA(clause, ScalarArrayOpExpr))
2085 ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
2086 Oid saop_op = saop->opno;
2087 Oid saop_coll = saop->inputcollid;
2088 Expr *leftop = (Expr *) linitial(saop->args),
2089 *rightop = (Expr *) lsecond(saop->args);
2090 List *elem_exprs,
2091 *elem_clauses;
2092 ListCell *lc1;
2094 if (IsA(leftop, RelabelType))
2095 leftop = ((RelabelType *) leftop)->arg;
2097 /* check if the LHS matches this partition key */
2098 if (!equal(leftop, partkey) ||
2099 !PartCollMatchesExprColl(partcoll, saop->inputcollid))
2100 return PARTCLAUSE_NOMATCH;
2103 * See if the operator is relevant to the partitioning opfamily.
2105 * In case of NOT IN (..), we get a '<>', which we handle if list
2106 * partitioning is in use and we're able to confirm that it's negator
2107 * is a btree equality operator belonging to the partitioning operator
2108 * family. As above, report NOMATCH for non-matching operator.
2110 if (!op_in_opfamily(saop_op, partopfamily))
2112 Oid negator;
2114 if (part_scheme->strategy != PARTITION_STRATEGY_LIST)
2115 return PARTCLAUSE_NOMATCH;
2117 negator = get_negator(saop_op);
2118 if (OidIsValid(negator) && op_in_opfamily(negator, partopfamily))
2120 int strategy;
2121 Oid lefttype,
2122 righttype;
2124 get_op_opfamily_properties(negator, partopfamily,
2125 false, &strategy,
2126 &lefttype, &righttype);
2127 if (strategy != BTEqualStrategyNumber)
2128 return PARTCLAUSE_NOMATCH;
2130 else
2131 return PARTCLAUSE_NOMATCH; /* no useful negator */
2135 * Only allow strict operators. This will guarantee nulls are
2136 * filtered. (This test is likely useless, since btree and hash
2137 * comparison operators are generally strict.)
2139 if (!op_strict(saop_op))
2140 return PARTCLAUSE_UNSUPPORTED;
2143 * OK, we have a match to the partition key and a suitable operator.
2144 * Examine the array argument to see if it's usable for pruning. This
2145 * is identical to the logic for a plain OpExpr.
2147 if (!IsA(rightop, Const))
2149 Bitmapset *paramids;
2152 * When pruning in the planner, we only support pruning using
2153 * comparisons to constants. We cannot prune on the basis of
2154 * anything that's not immutable. (Note that has_mutable_arg and
2155 * has_exec_param do not get set for this target value.)
2157 if (context->target == PARTTARGET_PLANNER)
2158 return PARTCLAUSE_UNSUPPORTED;
2161 * We can never prune using an expression that contains Vars.
2163 if (contain_var_clause((Node *) rightop))
2164 return PARTCLAUSE_UNSUPPORTED;
2167 * And we must reject anything containing a volatile function.
2168 * Stable functions are OK though.
2170 if (contain_volatile_functions((Node *) rightop))
2171 return PARTCLAUSE_UNSUPPORTED;
2174 * See if there are any exec Params. If so, we can only use this
2175 * expression during per-scan pruning.
2177 paramids = pull_exec_paramids(rightop);
2178 if (!bms_is_empty(paramids))
2180 context->has_exec_param = true;
2181 if (context->target != PARTTARGET_EXEC)
2182 return PARTCLAUSE_UNSUPPORTED;
2184 else
2186 /* It's potentially usable, but mutable */
2187 context->has_mutable_arg = true;
2192 * Check whether the comparison operator itself is immutable. (We
2193 * assume anything that's in a btree or hash opclass is at least
2194 * stable, but we need to check for immutability.)
2196 if (op_volatile(saop_op) != PROVOLATILE_IMMUTABLE)
2198 context->has_mutable_op = true;
2201 * When pruning in the planner, we cannot prune with mutable
2202 * operators.
2204 if (context->target == PARTTARGET_PLANNER)
2205 return PARTCLAUSE_UNSUPPORTED;
2209 * Examine the contents of the array argument.
2211 elem_exprs = NIL;
2212 if (IsA(rightop, Const))
2215 * For a constant array, convert the elements to a list of Const
2216 * nodes, one for each array element (excepting nulls).
2218 Const *arr = (Const *) rightop;
2219 ArrayType *arrval;
2220 int16 elemlen;
2221 bool elembyval;
2222 char elemalign;
2223 Datum *elem_values;
2224 bool *elem_nulls;
2225 int num_elems,
2228 /* If the array itself is null, the saop returns null */
2229 if (arr->constisnull)
2230 return PARTCLAUSE_MATCH_CONTRADICT;
2232 arrval = DatumGetArrayTypeP(arr->constvalue);
2233 get_typlenbyvalalign(ARR_ELEMTYPE(arrval),
2234 &elemlen, &elembyval, &elemalign);
2235 deconstruct_array(arrval,
2236 ARR_ELEMTYPE(arrval),
2237 elemlen, elembyval, elemalign,
2238 &elem_values, &elem_nulls,
2239 &num_elems);
2240 for (i = 0; i < num_elems; i++)
2242 Const *elem_expr;
2245 * A null array element must lead to a null comparison result,
2246 * since saop_op is known strict. We can ignore it in the
2247 * useOr case, but otherwise it implies self-contradiction.
2249 if (elem_nulls[i])
2251 if (saop->useOr)
2252 continue;
2253 return PARTCLAUSE_MATCH_CONTRADICT;
2256 elem_expr = makeConst(ARR_ELEMTYPE(arrval), -1,
2257 arr->constcollid, elemlen,
2258 elem_values[i], false, elembyval);
2259 elem_exprs = lappend(elem_exprs, elem_expr);
2262 else if (IsA(rightop, ArrayExpr))
2264 ArrayExpr *arrexpr = castNode(ArrayExpr, rightop);
2267 * For a nested ArrayExpr, we don't know how to get the actual
2268 * scalar values out into a flat list, so we give up doing
2269 * anything with this ScalarArrayOpExpr.
2271 if (arrexpr->multidims)
2272 return PARTCLAUSE_UNSUPPORTED;
2275 * Otherwise, we can just use the list of element values.
2277 elem_exprs = arrexpr->elements;
2279 else
2281 /* Give up on any other clause types. */
2282 return PARTCLAUSE_UNSUPPORTED;
2286 * Now generate a list of clauses, one for each array element, of the
2287 * form leftop saop_op elem_expr
2289 elem_clauses = NIL;
2290 foreach(lc1, elem_exprs)
2292 Expr *rightop = (Expr *) lfirst(lc1),
2293 *elem_clause;
2295 elem_clause = make_opclause(saop_op, BOOLOID, false,
2296 leftop, rightop,
2297 InvalidOid, saop_coll);
2298 elem_clauses = lappend(elem_clauses, elem_clause);
2302 * If we have an ANY clause and multiple elements, now turn the list
2303 * of clauses into an OR expression.
2305 if (saop->useOr && list_length(elem_clauses) > 1)
2306 elem_clauses = list_make1(makeBoolExpr(OR_EXPR, elem_clauses, -1));
2308 /* Finally, generate steps */
2309 *clause_steps = gen_partprune_steps_internal(context, elem_clauses);
2310 if (context->contradictory)
2311 return PARTCLAUSE_MATCH_CONTRADICT;
2312 else if (*clause_steps == NIL)
2313 return PARTCLAUSE_UNSUPPORTED; /* step generation failed */
2314 return PARTCLAUSE_MATCH_STEPS;
2316 else if (IsA(clause, NullTest))
2318 NullTest *nulltest = (NullTest *) clause;
2319 Expr *arg = nulltest->arg;
2321 if (IsA(arg, RelabelType))
2322 arg = ((RelabelType *) arg)->arg;
2324 /* Does arg match with this partition key column? */
2325 if (!equal(arg, partkey))
2326 return PARTCLAUSE_NOMATCH;
2328 *clause_is_not_null = (nulltest->nulltesttype == IS_NOT_NULL);
2330 return PARTCLAUSE_MATCH_NULLNESS;
2334 * If we get here then the return value depends on the result of the
2335 * match_boolean_partition_clause call above. If the call returned
2336 * PARTCLAUSE_UNSUPPORTED then we're either not dealing with a bool qual
2337 * or the bool qual is not suitable for pruning. Since the qual didn't
2338 * match up to any of the other qual types supported here, then trying to
2339 * match it against any other partition key is a waste of time, so just
2340 * return PARTCLAUSE_UNSUPPORTED. If the qual just couldn't be matched to
2341 * this partition key, then it may match another, so return
2342 * PARTCLAUSE_NOMATCH. The only other value that
2343 * match_boolean_partition_clause can return is PARTCLAUSE_MATCH_CLAUSE,
2344 * and since that value was already dealt with above, then we can just
2345 * return boolmatchstatus.
2347 return boolmatchstatus;
2351 * get_steps_using_prefix
2352 * Generate list of PartitionPruneStepOp steps each consisting of given
2353 * opstrategy
2355 * To generate steps, step_lastexpr and step_lastcmpfn are appended to
2356 * expressions and cmpfns, respectively, extracted from the clauses in
2357 * 'prefix'. Actually, since 'prefix' may contain multiple clauses for the
2358 * same partition key column, we must generate steps for various combinations
2359 * of the clauses of different keys.
2361 * For list/range partitioning, callers must ensure that step_nullkeys is
2362 * NULL, and that prefix contains at least one clause for each of the
2363 * partition keys earlier than one specified in step_lastkeyno if it's
2364 * greater than zero. For hash partitioning, step_nullkeys is allowed to be
2365 * non-NULL, but they must ensure that prefix contains at least one clause
2366 * for each of the partition keys other than those specified in step_nullkeys
2367 * and step_lastkeyno.
2369 * For both cases, callers must also ensure that clauses in prefix are sorted
2370 * in ascending order of their partition key numbers.
2372 static List *
2373 get_steps_using_prefix(GeneratePruningStepsContext *context,
2374 StrategyNumber step_opstrategy,
2375 bool step_op_is_ne,
2376 Expr *step_lastexpr,
2377 Oid step_lastcmpfn,
2378 int step_lastkeyno,
2379 Bitmapset *step_nullkeys,
2380 List *prefix)
2382 Assert(step_nullkeys == NULL ||
2383 context->rel->part_scheme->strategy == PARTITION_STRATEGY_HASH);
2385 /* Quick exit if there are no values to prefix with. */
2386 if (prefix == NIL)
2388 PartitionPruneStep *step;
2390 step = gen_prune_step_op(context,
2391 step_opstrategy,
2392 step_op_is_ne,
2393 list_make1(step_lastexpr),
2394 list_make1_oid(step_lastcmpfn),
2395 step_nullkeys);
2396 return list_make1(step);
2399 /* Recurse to generate steps for various combinations. */
2400 return get_steps_using_prefix_recurse(context,
2401 step_opstrategy,
2402 step_op_is_ne,
2403 step_lastexpr,
2404 step_lastcmpfn,
2405 step_lastkeyno,
2406 step_nullkeys,
2407 prefix,
2408 list_head(prefix),
2409 NIL, NIL);
2413 * get_steps_using_prefix_recurse
2414 * Recursively generate combinations of clauses for different partition
2415 * keys and start generating steps upon reaching clauses for the greatest
2416 * column that is less than the one for which we're currently generating
2417 * steps (that is, step_lastkeyno)
2419 * 'prefix' is the list of PartClauseInfos.
2420 * 'start' is where we should start iterating for the current invocation.
2421 * 'step_exprs' and 'step_cmpfns' each contains the expressions and cmpfns
2422 * we've generated so far from the clauses for the previous part keys.
2424 static List *
2425 get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
2426 StrategyNumber step_opstrategy,
2427 bool step_op_is_ne,
2428 Expr *step_lastexpr,
2429 Oid step_lastcmpfn,
2430 int step_lastkeyno,
2431 Bitmapset *step_nullkeys,
2432 List *prefix,
2433 ListCell *start,
2434 List *step_exprs,
2435 List *step_cmpfns)
2437 List *result = NIL;
2438 ListCell *lc;
2439 int cur_keyno;
2441 /* Actually, recursion would be limited by PARTITION_MAX_KEYS. */
2442 check_stack_depth();
2444 /* Check if we need to recurse. */
2445 Assert(start != NULL);
2446 cur_keyno = ((PartClauseInfo *) lfirst(start))->keyno;
2447 if (cur_keyno < step_lastkeyno - 1)
2449 PartClauseInfo *pc;
2450 ListCell *next_start;
2453 * For each clause with cur_keyno, add its expr and cmpfn to
2454 * step_exprs and step_cmpfns, respectively, and recurse after setting
2455 * next_start to the ListCell of the first clause for the next
2456 * partition key.
2458 for_each_cell(lc, prefix, start)
2460 pc = lfirst(lc);
2462 if (pc->keyno > cur_keyno)
2463 break;
2465 next_start = lc;
2467 for_each_cell(lc, prefix, start)
2469 List *moresteps;
2470 List *step_exprs1,
2471 *step_cmpfns1;
2473 pc = lfirst(lc);
2474 if (pc->keyno == cur_keyno)
2476 /* Leave the original step_exprs unmodified. */
2477 step_exprs1 = list_copy(step_exprs);
2478 step_exprs1 = lappend(step_exprs1, pc->expr);
2480 /* Leave the original step_cmpfns unmodified. */
2481 step_cmpfns1 = list_copy(step_cmpfns);
2482 step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2484 else
2486 Assert(pc->keyno > cur_keyno);
2487 break;
2490 moresteps = get_steps_using_prefix_recurse(context,
2491 step_opstrategy,
2492 step_op_is_ne,
2493 step_lastexpr,
2494 step_lastcmpfn,
2495 step_lastkeyno,
2496 step_nullkeys,
2497 prefix,
2498 next_start,
2499 step_exprs1,
2500 step_cmpfns1);
2501 result = list_concat(result, moresteps);
2503 list_free(step_exprs1);
2504 list_free(step_cmpfns1);
2507 else
2510 * End the current recursion cycle and start generating steps, one for
2511 * each clause with cur_keyno, which is all clauses from here onward
2512 * till the end of the list. Note that for hash partitioning,
2513 * step_nullkeys is allowed to be non-empty, in which case step_exprs
2514 * would only contain expressions for the earlier partition keys that
2515 * are not specified in step_nullkeys.
2517 Assert(list_length(step_exprs) == cur_keyno ||
2518 !bms_is_empty(step_nullkeys));
2521 * Note also that for hash partitioning, each partition key should
2522 * have either equality clauses or an IS NULL clause, so if a
2523 * partition key doesn't have an expression, it would be specified in
2524 * step_nullkeys.
2526 Assert(context->rel->part_scheme->strategy
2527 != PARTITION_STRATEGY_HASH ||
2528 list_length(step_exprs) + 2 + bms_num_members(step_nullkeys) ==
2529 context->rel->part_scheme->partnatts);
2530 for_each_cell(lc, prefix, start)
2532 PartClauseInfo *pc = lfirst(lc);
2533 PartitionPruneStep *step;
2534 List *step_exprs1,
2535 *step_cmpfns1;
2537 Assert(pc->keyno == cur_keyno);
2539 /* Leave the original step_exprs unmodified. */
2540 step_exprs1 = list_copy(step_exprs);
2541 step_exprs1 = lappend(step_exprs1, pc->expr);
2542 step_exprs1 = lappend(step_exprs1, step_lastexpr);
2544 /* Leave the original step_cmpfns unmodified. */
2545 step_cmpfns1 = list_copy(step_cmpfns);
2546 step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2547 step_cmpfns1 = lappend_oid(step_cmpfns1, step_lastcmpfn);
2549 step = gen_prune_step_op(context,
2550 step_opstrategy, step_op_is_ne,
2551 step_exprs1, step_cmpfns1,
2552 step_nullkeys);
2553 result = lappend(result, step);
2557 return result;
2561 * get_matching_hash_bounds
2562 * Determine offset of the hash bound matching the specified values,
2563 * considering that all the non-null values come from clauses containing
2564 * a compatible hash equality operator and any keys that are null come
2565 * from an IS NULL clause.
2567 * Generally this function will return a single matching bound offset,
2568 * although if a partition has not been setup for a given modulus then we may
2569 * return no matches. If the number of clauses found don't cover the entire
2570 * partition key, then we'll need to return all offsets.
2572 * 'opstrategy' if non-zero must be HTEqualStrategyNumber.
2574 * 'values' contains Datums indexed by the partition key to use for pruning.
2576 * 'nvalues', the number of Datums in the 'values' array.
2578 * 'partsupfunc' contains partition hashing functions that can produce correct
2579 * hash for the type of the values contained in 'values'.
2581 * 'nullkeys' is the set of partition keys that are null.
2583 static PruneStepResult *
2584 get_matching_hash_bounds(PartitionPruneContext *context,
2585 StrategyNumber opstrategy, Datum *values, int nvalues,
2586 FmgrInfo *partsupfunc, Bitmapset *nullkeys)
2588 PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2589 PartitionBoundInfo boundinfo = context->boundinfo;
2590 int *partindices = boundinfo->indexes;
2591 int partnatts = context->partnatts;
2592 bool isnull[PARTITION_MAX_KEYS];
2593 int i;
2594 uint64 rowHash;
2595 int greatest_modulus;
2596 Oid *partcollation = context->partcollation;
2598 Assert(context->strategy == PARTITION_STRATEGY_HASH);
2601 * For hash partitioning we can only perform pruning based on equality
2602 * clauses to the partition key or IS NULL clauses. We also can only
2603 * prune if we got values for all keys.
2605 if (nvalues + bms_num_members(nullkeys) == partnatts)
2608 * If there are any values, they must have come from clauses
2609 * containing an equality operator compatible with hash partitioning.
2611 Assert(opstrategy == HTEqualStrategyNumber || nvalues == 0);
2613 for (i = 0; i < partnatts; i++)
2614 isnull[i] = bms_is_member(i, nullkeys);
2616 rowHash = compute_partition_hash_value(partnatts, partsupfunc, partcollation,
2617 values, isnull);
2619 greatest_modulus = boundinfo->nindexes;
2620 if (partindices[rowHash % greatest_modulus] >= 0)
2621 result->bound_offsets =
2622 bms_make_singleton(rowHash % greatest_modulus);
2624 else
2626 /* Report all valid offsets into the boundinfo->indexes array. */
2627 result->bound_offsets = bms_add_range(NULL, 0,
2628 boundinfo->nindexes - 1);
2632 * There is neither a special hash null partition or the default hash
2633 * partition.
2635 result->scan_null = result->scan_default = false;
2637 return result;
2641 * get_matching_list_bounds
2642 * Determine the offsets of list bounds matching the specified value,
2643 * according to the semantics of the given operator strategy
2645 * scan_default will be set in the returned struct, if the default partition
2646 * needs to be scanned, provided one exists at all. scan_null will be set if
2647 * the special null-accepting partition needs to be scanned.
2649 * 'opstrategy' if non-zero must be a btree strategy number.
2651 * 'value' contains the value to use for pruning.
2653 * 'nvalues', if non-zero, should be exactly 1, because of list partitioning.
2655 * 'partsupfunc' contains the list partitioning comparison function to be used
2656 * to perform partition_list_bsearch
2658 * 'nullkeys' is the set of partition keys that are null.
2660 static PruneStepResult *
2661 get_matching_list_bounds(PartitionPruneContext *context,
2662 StrategyNumber opstrategy, Datum value, int nvalues,
2663 FmgrInfo *partsupfunc, Bitmapset *nullkeys)
2665 PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2666 PartitionBoundInfo boundinfo = context->boundinfo;
2667 int off,
2668 minoff,
2669 maxoff;
2670 bool is_equal;
2671 bool inclusive = false;
2672 Oid *partcollation = context->partcollation;
2674 Assert(context->strategy == PARTITION_STRATEGY_LIST);
2675 Assert(context->partnatts == 1);
2677 result->scan_null = result->scan_default = false;
2679 if (!bms_is_empty(nullkeys))
2682 * Nulls may exist in only one partition - the partition whose
2683 * accepted set of values includes null or the default partition if
2684 * the former doesn't exist.
2686 if (partition_bound_accepts_nulls(boundinfo))
2687 result->scan_null = true;
2688 else
2689 result->scan_default = partition_bound_has_default(boundinfo);
2690 return result;
2694 * If there are no datums to compare keys with, but there are partitions,
2695 * just return the default partition if one exists.
2697 if (boundinfo->ndatums == 0)
2699 result->scan_default = partition_bound_has_default(boundinfo);
2700 return result;
2703 minoff = 0;
2704 maxoff = boundinfo->ndatums - 1;
2707 * If there are no values to compare with the datums in boundinfo, it
2708 * means the caller asked for partitions for all non-null datums. Add
2709 * indexes of *all* partitions, including the default if any.
2711 if (nvalues == 0)
2713 Assert(boundinfo->ndatums > 0);
2714 result->bound_offsets = bms_add_range(NULL, 0,
2715 boundinfo->ndatums - 1);
2716 result->scan_default = partition_bound_has_default(boundinfo);
2717 return result;
2720 /* Special case handling of values coming from a <> operator clause. */
2721 if (opstrategy == InvalidStrategy)
2724 * First match to all bounds. We'll remove any matching datums below.
2726 Assert(boundinfo->ndatums > 0);
2727 result->bound_offsets = bms_add_range(NULL, 0,
2728 boundinfo->ndatums - 1);
2730 off = partition_list_bsearch(partsupfunc, partcollation, boundinfo,
2731 value, &is_equal);
2732 if (off >= 0 && is_equal)
2735 /* We have a match. Remove from the result. */
2736 Assert(boundinfo->indexes[off] >= 0);
2737 result->bound_offsets = bms_del_member(result->bound_offsets,
2738 off);
2741 /* Always include the default partition if any. */
2742 result->scan_default = partition_bound_has_default(boundinfo);
2744 return result;
2748 * With range queries, always include the default list partition, because
2749 * list partitions divide the key space in a discontinuous manner, not all
2750 * values in the given range will have a partition assigned. This may not
2751 * technically be true for some data types (e.g. integer types), however,
2752 * we currently lack any sort of infrastructure to provide us with proofs
2753 * that would allow us to do anything smarter here.
2755 if (opstrategy != BTEqualStrategyNumber)
2756 result->scan_default = partition_bound_has_default(boundinfo);
2758 switch (opstrategy)
2760 case BTEqualStrategyNumber:
2761 off = partition_list_bsearch(partsupfunc,
2762 partcollation,
2763 boundinfo, value,
2764 &is_equal);
2765 if (off >= 0 && is_equal)
2767 Assert(boundinfo->indexes[off] >= 0);
2768 result->bound_offsets = bms_make_singleton(off);
2770 else
2771 result->scan_default = partition_bound_has_default(boundinfo);
2772 return result;
2774 case BTGreaterEqualStrategyNumber:
2775 inclusive = true;
2776 /* fall through */
2777 case BTGreaterStrategyNumber:
2778 off = partition_list_bsearch(partsupfunc,
2779 partcollation,
2780 boundinfo, value,
2781 &is_equal);
2782 if (off >= 0)
2784 /* We don't want the matched datum to be in the result. */
2785 if (!is_equal || !inclusive)
2786 off++;
2788 else
2791 * This case means all partition bounds are greater, which in
2792 * turn means that all partitions satisfy this key.
2794 off = 0;
2798 * off is greater than the numbers of datums we have partitions
2799 * for. The only possible partition that could contain a match is
2800 * the default partition, but we must've set context->scan_default
2801 * above anyway if one exists.
2803 if (off > boundinfo->ndatums - 1)
2804 return result;
2806 minoff = off;
2807 break;
2809 case BTLessEqualStrategyNumber:
2810 inclusive = true;
2811 /* fall through */
2812 case BTLessStrategyNumber:
2813 off = partition_list_bsearch(partsupfunc,
2814 partcollation,
2815 boundinfo, value,
2816 &is_equal);
2817 if (off >= 0 && is_equal && !inclusive)
2818 off--;
2821 * off is smaller than the datums of all non-default partitions.
2822 * The only possible partition that could contain a match is the
2823 * default partition, but we must've set context->scan_default
2824 * above anyway if one exists.
2826 if (off < 0)
2827 return result;
2829 maxoff = off;
2830 break;
2832 default:
2833 elog(ERROR, "invalid strategy number %d", opstrategy);
2834 break;
2837 Assert(minoff >= 0 && maxoff >= 0);
2838 result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
2839 return result;
2844 * get_matching_range_bounds
2845 * Determine the offsets of range bounds matching the specified values,
2846 * according to the semantics of the given operator strategy
2848 * Each datum whose offset is in result is to be treated as the upper bound of
2849 * the partition that will contain the desired values.
2851 * scan_default is set in the returned struct if a default partition exists
2852 * and we're absolutely certain that it needs to be scanned. We do *not* set
2853 * it just because values match portions of the key space uncovered by
2854 * partitions other than default (space which we normally assume to belong to
2855 * the default partition): the final set of bounds obtained after combining
2856 * multiple pruning steps might exclude it, so we infer its inclusion
2857 * elsewhere.
2859 * 'opstrategy' if non-zero must be a btree strategy number.
2861 * 'values' contains Datums indexed by the partition key to use for pruning.
2863 * 'nvalues', number of Datums in 'values' array. Must be <= context->partnatts.
2865 * 'partsupfunc' contains the range partitioning comparison functions to be
2866 * used to perform partition_range_datum_bsearch or partition_rbound_datum_cmp
2867 * using.
2869 * 'nullkeys' is the set of partition keys that are null.
2871 static PruneStepResult *
2872 get_matching_range_bounds(PartitionPruneContext *context,
2873 StrategyNumber opstrategy, Datum *values, int nvalues,
2874 FmgrInfo *partsupfunc, Bitmapset *nullkeys)
2876 PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2877 PartitionBoundInfo boundinfo = context->boundinfo;
2878 Oid *partcollation = context->partcollation;
2879 int partnatts = context->partnatts;
2880 int *partindices = boundinfo->indexes;
2881 int off,
2882 minoff,
2883 maxoff;
2884 bool is_equal;
2885 bool inclusive = false;
2887 Assert(context->strategy == PARTITION_STRATEGY_RANGE);
2888 Assert(nvalues <= partnatts);
2890 result->scan_null = result->scan_default = false;
2893 * If there are no datums to compare keys with, or if we got an IS NULL
2894 * clause just return the default partition, if it exists.
2896 if (boundinfo->ndatums == 0 || !bms_is_empty(nullkeys))
2898 result->scan_default = partition_bound_has_default(boundinfo);
2899 return result;
2902 minoff = 0;
2903 maxoff = boundinfo->ndatums;
2906 * If there are no values to compare with the datums in boundinfo, it
2907 * means the caller asked for partitions for all non-null datums. Add
2908 * indexes of *all* partitions, including the default partition if one
2909 * exists.
2911 if (nvalues == 0)
2913 /* ignore key space not covered by any partitions */
2914 if (partindices[minoff] < 0)
2915 minoff++;
2916 if (partindices[maxoff] < 0)
2917 maxoff--;
2919 result->scan_default = partition_bound_has_default(boundinfo);
2920 Assert(partindices[minoff] >= 0 &&
2921 partindices[maxoff] >= 0);
2922 result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
2924 return result;
2928 * If the query does not constrain all key columns, we'll need to scan the
2929 * default partition, if any.
2931 if (nvalues < partnatts)
2932 result->scan_default = partition_bound_has_default(boundinfo);
2934 switch (opstrategy)
2936 case BTEqualStrategyNumber:
2937 /* Look for the smallest bound that is = lookup value. */
2938 off = partition_range_datum_bsearch(partsupfunc,
2939 partcollation,
2940 boundinfo,
2941 nvalues, values,
2942 &is_equal);
2944 if (off >= 0 && is_equal)
2946 if (nvalues == partnatts)
2948 /* There can only be zero or one matching partition. */
2949 result->bound_offsets = bms_make_singleton(off + 1);
2950 return result;
2952 else
2954 int saved_off = off;
2957 * Since the lookup value contains only a prefix of keys,
2958 * we must find other bounds that may also match the
2959 * prefix. partition_range_datum_bsearch() returns the
2960 * offset of one of them, find others by checking adjacent
2961 * bounds.
2965 * First find greatest bound that's smaller than the
2966 * lookup value.
2968 while (off >= 1)
2970 int32 cmpval;
2972 cmpval =
2973 partition_rbound_datum_cmp(partsupfunc,
2974 partcollation,
2975 boundinfo->datums[off - 1],
2976 boundinfo->kind[off - 1],
2977 values, nvalues);
2978 if (cmpval != 0)
2979 break;
2980 off--;
2983 Assert(0 ==
2984 partition_rbound_datum_cmp(partsupfunc,
2985 partcollation,
2986 boundinfo->datums[off],
2987 boundinfo->kind[off],
2988 values, nvalues));
2991 * We can treat 'off' as the offset of the smallest bound
2992 * to be included in the result, if we know it is the
2993 * upper bound of the partition in which the lookup value
2994 * could possibly exist. One case it couldn't is if the
2995 * bound, or precisely the matched portion of its prefix,
2996 * is not inclusive.
2998 if (boundinfo->kind[off][nvalues] ==
2999 PARTITION_RANGE_DATUM_MINVALUE)
3000 off++;
3002 minoff = off;
3005 * Now find smallest bound that's greater than the lookup
3006 * value.
3008 off = saved_off;
3009 while (off < boundinfo->ndatums - 1)
3011 int32 cmpval;
3013 cmpval = partition_rbound_datum_cmp(partsupfunc,
3014 partcollation,
3015 boundinfo->datums[off + 1],
3016 boundinfo->kind[off + 1],
3017 values, nvalues);
3018 if (cmpval != 0)
3019 break;
3020 off++;
3023 Assert(0 ==
3024 partition_rbound_datum_cmp(partsupfunc,
3025 partcollation,
3026 boundinfo->datums[off],
3027 boundinfo->kind[off],
3028 values, nvalues));
3031 * off + 1, then would be the offset of the greatest bound
3032 * to be included in the result.
3034 maxoff = off + 1;
3037 Assert(minoff >= 0 && maxoff >= 0);
3038 result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3040 else
3043 * The lookup value falls in the range between some bounds in
3044 * boundinfo. 'off' would be the offset of the greatest bound
3045 * that is <= lookup value, so add off + 1 to the result
3046 * instead as the offset of the upper bound of the only
3047 * partition that may contain the lookup value. If 'off' is
3048 * -1 indicating that all bounds are greater, then we simply
3049 * end up adding the first bound's offset, that is, 0.
3051 result->bound_offsets = bms_make_singleton(off + 1);
3054 return result;
3056 case BTGreaterEqualStrategyNumber:
3057 inclusive = true;
3058 /* fall through */
3059 case BTGreaterStrategyNumber:
3062 * Look for the smallest bound that is > or >= lookup value and
3063 * set minoff to its offset.
3065 off = partition_range_datum_bsearch(partsupfunc,
3066 partcollation,
3067 boundinfo,
3068 nvalues, values,
3069 &is_equal);
3070 if (off < 0)
3073 * All bounds are greater than the lookup value, so include
3074 * all of them in the result.
3076 minoff = 0;
3078 else
3080 if (is_equal && nvalues < partnatts)
3083 * Since the lookup value contains only a prefix of keys,
3084 * we must find other bounds that may also match the
3085 * prefix. partition_range_datum_bsearch() returns the
3086 * offset of one of them, find others by checking adjacent
3087 * bounds.
3089 * Based on whether the lookup values are inclusive or
3090 * not, we must either include the indexes of all such
3091 * bounds in the result (that is, set minoff to the index
3092 * of smallest such bound) or find the smallest one that's
3093 * greater than the lookup values and set minoff to that.
3095 while (off >= 1 && off < boundinfo->ndatums - 1)
3097 int32 cmpval;
3098 int nextoff;
3100 nextoff = inclusive ? off - 1 : off + 1;
3101 cmpval =
3102 partition_rbound_datum_cmp(partsupfunc,
3103 partcollation,
3104 boundinfo->datums[nextoff],
3105 boundinfo->kind[nextoff],
3106 values, nvalues);
3107 if (cmpval != 0)
3108 break;
3110 off = nextoff;
3113 Assert(0 ==
3114 partition_rbound_datum_cmp(partsupfunc,
3115 partcollation,
3116 boundinfo->datums[off],
3117 boundinfo->kind[off],
3118 values, nvalues));
3120 minoff = inclusive ? off : off + 1;
3122 else
3126 * lookup value falls in the range between some bounds in
3127 * boundinfo. off would be the offset of the greatest
3128 * bound that is <= lookup value, so add off + 1 to the
3129 * result instead as the offset of the upper bound of the
3130 * smallest partition that may contain the lookup value.
3132 minoff = off + 1;
3135 break;
3137 case BTLessEqualStrategyNumber:
3138 inclusive = true;
3139 /* fall through */
3140 case BTLessStrategyNumber:
3143 * Look for the greatest bound that is < or <= lookup value and
3144 * set maxoff to its offset.
3146 off = partition_range_datum_bsearch(partsupfunc,
3147 partcollation,
3148 boundinfo,
3149 nvalues, values,
3150 &is_equal);
3151 if (off >= 0)
3154 * See the comment above.
3156 if (is_equal && nvalues < partnatts)
3158 while (off >= 1 && off < boundinfo->ndatums - 1)
3160 int32 cmpval;
3161 int nextoff;
3163 nextoff = inclusive ? off + 1 : off - 1;
3164 cmpval = partition_rbound_datum_cmp(partsupfunc,
3165 partcollation,
3166 boundinfo->datums[nextoff],
3167 boundinfo->kind[nextoff],
3168 values, nvalues);
3169 if (cmpval != 0)
3170 break;
3172 off = nextoff;
3175 Assert(0 ==
3176 partition_rbound_datum_cmp(partsupfunc,
3177 partcollation,
3178 boundinfo->datums[off],
3179 boundinfo->kind[off],
3180 values, nvalues));
3182 maxoff = inclusive ? off + 1 : off;
3186 * The lookup value falls in the range between some bounds in
3187 * boundinfo. 'off' would be the offset of the greatest bound
3188 * that is <= lookup value, so add off + 1 to the result
3189 * instead as the offset of the upper bound of the greatest
3190 * partition that may contain lookup value. If the lookup
3191 * value had exactly matched the bound, but it isn't
3192 * inclusive, no need add the adjacent partition.
3194 else if (!is_equal || inclusive)
3195 maxoff = off + 1;
3196 else
3197 maxoff = off;
3199 else
3202 * 'off' is -1 indicating that all bounds are greater, so just
3203 * set the first bound's offset as maxoff.
3205 maxoff = off + 1;
3207 break;
3209 default:
3210 elog(ERROR, "invalid strategy number %d", opstrategy);
3211 break;
3214 Assert(minoff >= 0 && minoff <= boundinfo->ndatums);
3215 Assert(maxoff >= 0 && maxoff <= boundinfo->ndatums);
3218 * If the smallest partition to return has MINVALUE (negative infinity) as
3219 * its lower bound, increment it to point to the next finite bound
3220 * (supposedly its upper bound), so that we don't inadvertently end up
3221 * scanning the default partition.
3223 if (minoff < boundinfo->ndatums && partindices[minoff] < 0)
3225 int lastkey = nvalues - 1;
3227 if (boundinfo->kind[minoff][lastkey] ==
3228 PARTITION_RANGE_DATUM_MINVALUE)
3230 minoff++;
3231 Assert(boundinfo->indexes[minoff] >= 0);
3236 * If the previous greatest partition has MAXVALUE (positive infinity) as
3237 * its upper bound (something only possible to do with multi-column range
3238 * partitioning), we scan switch to it as the greatest partition to
3239 * return. Again, so that we don't inadvertently end up scanning the
3240 * default partition.
3242 if (maxoff >= 1 && partindices[maxoff] < 0)
3244 int lastkey = nvalues - 1;
3246 if (boundinfo->kind[maxoff - 1][lastkey] ==
3247 PARTITION_RANGE_DATUM_MAXVALUE)
3249 maxoff--;
3250 Assert(boundinfo->indexes[maxoff] >= 0);
3254 Assert(minoff >= 0 && maxoff >= 0);
3255 if (minoff <= maxoff)
3256 result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3258 return result;
3262 * pull_exec_paramids
3263 * Returns a Bitmapset containing the paramids of all Params with
3264 * paramkind = PARAM_EXEC in 'expr'.
3266 static Bitmapset *
3267 pull_exec_paramids(Expr *expr)
3269 Bitmapset *result = NULL;
3271 (void) pull_exec_paramids_walker((Node *) expr, &result);
3273 return result;
3276 static bool
3277 pull_exec_paramids_walker(Node *node, Bitmapset **context)
3279 if (node == NULL)
3280 return false;
3281 if (IsA(node, Param))
3283 Param *param = (Param *) node;
3285 if (param->paramkind == PARAM_EXEC)
3286 *context = bms_add_member(*context, param->paramid);
3287 return false;
3289 return expression_tree_walker(node, pull_exec_paramids_walker,
3290 (void *) context);
3294 * get_partkey_exec_paramids
3295 * Loop through given pruning steps and find out which exec Params
3296 * are used.
3298 * Returns a Bitmapset of Param IDs.
3300 static Bitmapset *
3301 get_partkey_exec_paramids(List *steps)
3303 Bitmapset *execparamids = NULL;
3304 ListCell *lc;
3306 foreach(lc, steps)
3308 PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc);
3309 ListCell *lc2;
3311 if (!IsA(step, PartitionPruneStepOp))
3312 continue;
3314 foreach(lc2, step->exprs)
3316 Expr *expr = lfirst(lc2);
3318 /* We can be quick for plain Consts */
3319 if (!IsA(expr, Const))
3320 execparamids = bms_join(execparamids,
3321 pull_exec_paramids(expr));
3325 return execparamids;
3329 * perform_pruning_base_step
3330 * Determines the indexes of datums that satisfy conditions specified in
3331 * 'opstep'.
3333 * Result also contains whether special null-accepting and/or default
3334 * partition need to be scanned.
3336 static PruneStepResult *
3337 perform_pruning_base_step(PartitionPruneContext *context,
3338 PartitionPruneStepOp *opstep)
3340 ListCell *lc1,
3341 *lc2;
3342 int keyno,
3343 nvalues;
3344 Datum values[PARTITION_MAX_KEYS];
3345 FmgrInfo *partsupfunc;
3346 int stateidx;
3349 * There better be the same number of expressions and compare functions.
3351 Assert(list_length(opstep->exprs) == list_length(opstep->cmpfns));
3353 nvalues = 0;
3354 lc1 = list_head(opstep->exprs);
3355 lc2 = list_head(opstep->cmpfns);
3358 * Generate the partition lookup key that will be used by one of the
3359 * get_matching_*_bounds functions called below.
3361 for (keyno = 0; keyno < context->partnatts; keyno++)
3364 * For hash partitioning, it is possible that values of some keys are
3365 * not provided in operator clauses, but instead the planner found
3366 * that they appeared in a IS NULL clause.
3368 if (bms_is_member(keyno, opstep->nullkeys))
3369 continue;
3372 * For range partitioning, we must only perform pruning with values
3373 * for either all partition keys or a prefix thereof.
3375 if (keyno > nvalues && context->strategy == PARTITION_STRATEGY_RANGE)
3376 break;
3378 if (lc1 != NULL)
3380 Expr *expr;
3381 Datum datum;
3382 bool isnull;
3383 Oid cmpfn;
3385 expr = lfirst(lc1);
3386 stateidx = PruneCxtStateIdx(context->partnatts,
3387 opstep->step.step_id, keyno);
3388 partkey_datum_from_expr(context, expr, stateidx,
3389 &datum, &isnull);
3392 * Since we only allow strict operators in pruning steps, any
3393 * null-valued comparison value must cause the comparison to fail,
3394 * so that no partitions could match.
3396 if (isnull)
3398 PruneStepResult *result;
3400 result = (PruneStepResult *) palloc(sizeof(PruneStepResult));
3401 result->bound_offsets = NULL;
3402 result->scan_default = false;
3403 result->scan_null = false;
3405 return result;
3408 /* Set up the stepcmpfuncs entry, unless we already did */
3409 cmpfn = lfirst_oid(lc2);
3410 Assert(OidIsValid(cmpfn));
3411 if (cmpfn != context->stepcmpfuncs[stateidx].fn_oid)
3414 * If the needed support function is the same one cached in
3415 * the relation's partition key, copy the cached FmgrInfo.
3416 * Otherwise (i.e., when we have a cross-type comparison), an
3417 * actual lookup is required.
3419 if (cmpfn == context->partsupfunc[keyno].fn_oid)
3420 fmgr_info_copy(&context->stepcmpfuncs[stateidx],
3421 &context->partsupfunc[keyno],
3422 context->ppccontext);
3423 else
3424 fmgr_info_cxt(cmpfn, &context->stepcmpfuncs[stateidx],
3425 context->ppccontext);
3428 values[keyno] = datum;
3429 nvalues++;
3431 lc1 = lnext(opstep->exprs, lc1);
3432 lc2 = lnext(opstep->cmpfns, lc2);
3437 * Point partsupfunc to the entry for the 0th key of this step; the
3438 * additional support functions, if any, follow consecutively.
3440 stateidx = PruneCxtStateIdx(context->partnatts, opstep->step.step_id, 0);
3441 partsupfunc = &context->stepcmpfuncs[stateidx];
3443 switch (context->strategy)
3445 case PARTITION_STRATEGY_HASH:
3446 return get_matching_hash_bounds(context,
3447 opstep->opstrategy,
3448 values, nvalues,
3449 partsupfunc,
3450 opstep->nullkeys);
3452 case PARTITION_STRATEGY_LIST:
3453 return get_matching_list_bounds(context,
3454 opstep->opstrategy,
3455 values[0], nvalues,
3456 &partsupfunc[0],
3457 opstep->nullkeys);
3459 case PARTITION_STRATEGY_RANGE:
3460 return get_matching_range_bounds(context,
3461 opstep->opstrategy,
3462 values, nvalues,
3463 partsupfunc,
3464 opstep->nullkeys);
3466 default:
3467 elog(ERROR, "unexpected partition strategy: %d",
3468 (int) context->strategy);
3469 break;
3472 return NULL;
3476 * perform_pruning_combine_step
3477 * Determines the indexes of datums obtained by combining those given
3478 * by the steps identified by cstep->source_stepids using the specified
3479 * combination method
3481 * Since cstep may refer to the result of earlier steps, we also receive
3482 * step_results here.
3484 static PruneStepResult *
3485 perform_pruning_combine_step(PartitionPruneContext *context,
3486 PartitionPruneStepCombine *cstep,
3487 PruneStepResult **step_results)
3489 PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
3490 bool firststep;
3491 ListCell *lc1;
3494 * A combine step without any source steps is an indication to not perform
3495 * any partition pruning. Return all datum indexes in that case.
3497 if (cstep->source_stepids == NIL)
3499 PartitionBoundInfo boundinfo = context->boundinfo;
3501 result->bound_offsets =
3502 bms_add_range(NULL, 0, boundinfo->nindexes - 1);
3503 result->scan_default = partition_bound_has_default(boundinfo);
3504 result->scan_null = partition_bound_accepts_nulls(boundinfo);
3505 return result;
3508 switch (cstep->combineOp)
3510 case PARTPRUNE_COMBINE_UNION:
3511 foreach(lc1, cstep->source_stepids)
3513 int step_id = lfirst_int(lc1);
3514 PruneStepResult *step_result;
3517 * step_results[step_id] must contain a valid result, which is
3518 * confirmed by the fact that cstep's step_id is greater than
3519 * step_id and the fact that results of the individual steps
3520 * are evaluated in sequence of their step_ids.
3522 if (step_id >= cstep->step.step_id)
3523 elog(ERROR, "invalid pruning combine step argument");
3524 step_result = step_results[step_id];
3525 Assert(step_result != NULL);
3527 /* Record any additional datum indexes from this step */
3528 result->bound_offsets = bms_add_members(result->bound_offsets,
3529 step_result->bound_offsets);
3531 /* Update whether to scan null and default partitions. */
3532 if (!result->scan_null)
3533 result->scan_null = step_result->scan_null;
3534 if (!result->scan_default)
3535 result->scan_default = step_result->scan_default;
3537 break;
3539 case PARTPRUNE_COMBINE_INTERSECT:
3540 firststep = true;
3541 foreach(lc1, cstep->source_stepids)
3543 int step_id = lfirst_int(lc1);
3544 PruneStepResult *step_result;
3546 if (step_id >= cstep->step.step_id)
3547 elog(ERROR, "invalid pruning combine step argument");
3548 step_result = step_results[step_id];
3549 Assert(step_result != NULL);
3551 if (firststep)
3553 /* Copy step's result the first time. */
3554 result->bound_offsets =
3555 bms_copy(step_result->bound_offsets);
3556 result->scan_null = step_result->scan_null;
3557 result->scan_default = step_result->scan_default;
3558 firststep = false;
3560 else
3562 /* Record datum indexes common to both steps */
3563 result->bound_offsets =
3564 bms_int_members(result->bound_offsets,
3565 step_result->bound_offsets);
3567 /* Update whether to scan null and default partitions. */
3568 if (result->scan_null)
3569 result->scan_null = step_result->scan_null;
3570 if (result->scan_default)
3571 result->scan_default = step_result->scan_default;
3574 break;
3577 return result;
3581 * match_boolean_partition_clause
3583 * If we're able to match the clause to the partition key as specially-shaped
3584 * boolean clause, set *outconst to a Const containing a true or false value
3585 * and return PARTCLAUSE_MATCH_CLAUSE. Returns PARTCLAUSE_UNSUPPORTED if the
3586 * clause is not a boolean clause or if the boolean clause is unsuitable for
3587 * partition pruning. Returns PARTCLAUSE_NOMATCH if it's a bool quals but
3588 * just does not match this partition key. *outconst is set to NULL in the
3589 * latter two cases.
3591 static PartClauseMatchStatus
3592 match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
3593 Expr **outconst)
3595 Expr *leftop;
3597 *outconst = NULL;
3600 * Partitioning currently can only use built-in AMs, so checking for
3601 * built-in boolean opfamilies is good enough.
3603 if (!IsBuiltinBooleanOpfamily(partopfamily))
3604 return PARTCLAUSE_UNSUPPORTED;
3606 if (IsA(clause, BooleanTest))
3608 BooleanTest *btest = (BooleanTest *) clause;
3610 /* Only IS [NOT] TRUE/FALSE are any good to us */
3611 if (btest->booltesttype == IS_UNKNOWN ||
3612 btest->booltesttype == IS_NOT_UNKNOWN)
3613 return PARTCLAUSE_UNSUPPORTED;
3615 leftop = btest->arg;
3616 if (IsA(leftop, RelabelType))
3617 leftop = ((RelabelType *) leftop)->arg;
3619 if (equal(leftop, partkey))
3620 *outconst = (btest->booltesttype == IS_TRUE ||
3621 btest->booltesttype == IS_NOT_FALSE)
3622 ? (Expr *) makeBoolConst(true, false)
3623 : (Expr *) makeBoolConst(false, false);
3625 if (*outconst)
3626 return PARTCLAUSE_MATCH_CLAUSE;
3628 else
3630 bool is_not_clause = is_notclause(clause);
3632 leftop = is_not_clause ? get_notclausearg(clause) : clause;
3634 if (IsA(leftop, RelabelType))
3635 leftop = ((RelabelType *) leftop)->arg;
3637 /* Compare to the partition key, and make up a clause ... */
3638 if (equal(leftop, partkey))
3639 *outconst = is_not_clause ?
3640 (Expr *) makeBoolConst(false, false) :
3641 (Expr *) makeBoolConst(true, false);
3642 else if (equal(negate_clause((Node *) leftop), partkey))
3643 *outconst = (Expr *) makeBoolConst(false, false);
3645 if (*outconst)
3646 return PARTCLAUSE_MATCH_CLAUSE;
3649 return PARTCLAUSE_NOMATCH;
3653 * partkey_datum_from_expr
3654 * Evaluate expression for potential partition pruning
3656 * Evaluate 'expr'; set *value and *isnull to the resulting Datum and nullflag.
3658 * If expr isn't a Const, its ExprState is in stateidx of the context
3659 * exprstate array.
3661 * Note that the evaluated result may be in the per-tuple memory context of
3662 * context->exprcontext, and we may have leaked other memory there too.
3663 * This memory must be recovered by resetting that ExprContext after
3664 * we're done with the pruning operation (see execPartition.c).
3666 static void
3667 partkey_datum_from_expr(PartitionPruneContext *context,
3668 Expr *expr, int stateidx,
3669 Datum *value, bool *isnull)
3671 if (IsA(expr, Const))
3673 /* We can always determine the value of a constant */
3674 Const *con = (Const *) expr;
3676 *value = con->constvalue;
3677 *isnull = con->constisnull;
3679 else
3681 ExprState *exprstate;
3682 ExprContext *ectx;
3685 * We should never see a non-Const in a step unless the caller has
3686 * passed a valid ExprContext.
3688 * When context->planstate is valid, context->exprcontext is same as
3689 * context->planstate->ps_ExprContext.
3691 Assert(context->planstate != NULL || context->exprcontext != NULL);
3692 Assert(context->planstate == NULL ||
3693 (context->exprcontext == context->planstate->ps_ExprContext));
3695 exprstate = context->exprstates[stateidx];
3696 ectx = context->exprcontext;
3697 *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);