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