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