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