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