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