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