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