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