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