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