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