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