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