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