diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index 59f34f5cac..79494db3fb 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -39,6 +39,7 @@ #include "access/nbtree.h" #include "catalog/pg_operator.h" #include "catalog/pg_opfamily.h" +#include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "executor/executor.h" #include "miscadmin.h" @@ -83,6 +84,17 @@ typedef enum PartClauseMatchStatus PARTCLAUSE_UNSUPPORTED } PartClauseMatchStatus; +/* + * PruneEnvironment + * Provides the pruning code context about where the pruning steps + * will be executed. + */ +typedef enum PruneEnvironment +{ + PRUNEENV_PLANNER, + PRUNEENV_EXECUTOR +} PruneEnvironment; + /* * GeneratePruningStepsContext * Information about the current state of generation of "pruning steps" @@ -116,11 +128,11 @@ static List *make_partitionedrel_pruneinfo(PlannerInfo *root, int *relid_subplan_map, List *partitioned_rels, List *prunequal, Bitmapset **matchedsubplans); -static List *gen_partprune_steps(RelOptInfo *rel, List *clauses, - bool *contradictory); +static List *gen_partprune_steps(RelOptInfo *rel, PruneEnvironment pruneenv, + List *clauses, bool *contradictory); static List *gen_partprune_steps_internal(GeneratePruningStepsContext *context, - RelOptInfo *rel, List *clauses, - bool *contradictory); + RelOptInfo *rel, PruneEnvironment pruneenv, + List *clauses, bool *contradictory); static PartitionPruneStep *gen_prune_step_op(GeneratePruningStepsContext *context, StrategyNumber opstrategy, bool op_is_ne, List *exprs, List *cmpfns, Bitmapset *nullkeys); @@ -132,6 +144,7 @@ static PartitionPruneStep *gen_prune_steps_from_opexps(PartitionScheme part_sche List **keyclauses, Bitmapset *nullkeys); static PartClauseMatchStatus match_clause_to_partition_key(RelOptInfo *rel, GeneratePruningStepsContext *context, + PruneEnvironment pruneenv, Expr *clause, Expr *partkey, int partkeyidx, bool *clause_is_not_null, PartClauseInfo **pc, List **clause_steps); @@ -410,8 +423,8 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, } /* Convert pruning qual to pruning steps. */ - pruning_steps = gen_partprune_steps(subpart, partprunequal, - &contradictory); + pruning_steps = gen_partprune_steps(subpart, PRUNEENV_EXECUTOR, + partprunequal, &contradictory); if (contradictory) { @@ -523,13 +536,16 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, /* * gen_partprune_steps * Process 'clauses' (a rel's baserestrictinfo list of clauses) and return - * a list of "partition pruning steps" + * a list of "partition pruning steps". + * + * 'pruneenv' defines where the pruning steps will be executed. * * If the clauses in the input list are contradictory or there is a * pseudo-constant "false", *contradictory is set to true upon return. */ static List * -gen_partprune_steps(RelOptInfo *rel, List *clauses, bool *contradictory) +gen_partprune_steps(RelOptInfo *rel, PruneEnvironment pruneenv, List *clauses, + bool *contradictory) { GeneratePruningStepsContext context; @@ -567,7 +583,8 @@ gen_partprune_steps(RelOptInfo *rel, List *clauses, bool *contradictory) } /* Down into the rabbit-hole. */ - gen_partprune_steps_internal(&context, rel, clauses, contradictory); + gen_partprune_steps_internal(&context, rel, pruneenv, clauses, + contradictory); return context.steps; } @@ -605,7 +622,8 @@ prune_append_rel_partitions(RelOptInfo *rel) * Process clauses. If the clauses are found to be contradictory, we can * return the empty set. */ - pruning_steps = gen_partprune_steps(rel, clauses, &contradictory); + pruning_steps = gen_partprune_steps(rel, PRUNEENV_PLANNER, clauses, + &contradictory); if (contradictory) return NULL; @@ -761,8 +779,8 @@ get_matching_partitions(PartitionPruneContext *context, List *pruning_steps) */ static List * gen_partprune_steps_internal(GeneratePruningStepsContext *context, - RelOptInfo *rel, List *clauses, - bool *contradictory) + RelOptInfo *rel, PruneEnvironment pruneenv, + List *clauses, bool *contradictory) { PartitionScheme part_scheme = rel->part_scheme; List *keyclauses[PARTITION_MAX_KEYS]; @@ -822,6 +840,7 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context, argsteps = gen_partprune_steps_internal(context, rel, + pruneenv, list_make1(arg), &arg_contradictory); if (!arg_contradictory) @@ -906,7 +925,7 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context, * recurse and later combine the component partitions sets * using a combine step. */ - argsteps = gen_partprune_steps_internal(context, rel, args, + argsteps = gen_partprune_steps_internal(context, rel, pruneenv, args, contradictory); if (*contradictory) return NIL; @@ -948,7 +967,7 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context, PartClauseInfo *pc = NULL; List *clause_steps = NIL; - switch (match_clause_to_partition_key(rel, context, + switch (match_clause_to_partition_key(rel, context, pruneenv, clause, partkey, i, &clause_is_not_null, &pc, &clause_steps)) @@ -1562,6 +1581,7 @@ gen_prune_steps_from_opexps(PartitionScheme part_scheme, static PartClauseMatchStatus match_clause_to_partition_key(RelOptInfo *rel, GeneratePruningStepsContext *context, + PruneEnvironment pruneenv, Expr *clause, Expr *partkey, int partkeyidx, bool *clause_is_not_null, PartClauseInfo **pc, List **clause_steps) @@ -1651,13 +1671,39 @@ match_clause_to_partition_key(RelOptInfo *rel, * matter which partkey it's matched to. */ - /* - * We can't prune using an expression with Vars. (Report failure as - * UNSUPPORTED, not NOMATCH: as in the no-commutator case above, we - * now know there are Vars on both sides, so it's no good.) - */ - if (contain_var_clause((Node *) expr)) - return PARTCLAUSE_UNSUPPORTED; + if (pruneenv == PRUNEENV_PLANNER) + { + /* + * When pruning in the planner, we only support pruning using + * constants. Immutable functions will have been folded to consts + * already so we could be dealing with a Var or an unsupported + * function call. + */ + if (!IsA(expr, Const)) + return PARTCLAUSE_UNSUPPORTED; + + /* + * Only allow immutable comparison functions when called during + * planning. + */ + if (op_volatile(opno) != PROVOLATILE_IMMUTABLE) + return PARTCLAUSE_UNSUPPORTED; + } + else + { + /* + * Otherwise, non-consts are supported, but we can't prune using + * an expression with Vars. (Report failure as UNSUPPORTED, not + * NOMATCH: as in the no-commutator case above, we now know there + * are Vars on both sides, so it's no good.) + */ + if (contain_var_clause((Node *) expr)) + return PARTCLAUSE_UNSUPPORTED; + + /* And we must reject anything containing a volatile function. */ + if (contain_volatile_functions((Node *) expr)) + return PARTCLAUSE_UNSUPPORTED; + } /* * Only allow strict operators. This will guarantee nulls are @@ -1666,10 +1712,6 @@ match_clause_to_partition_key(RelOptInfo *rel, if (!op_strict(opno)) return PARTCLAUSE_UNSUPPORTED; - /* We can't use any volatile expressions to prune partitions. */ - if (contain_volatile_functions((Node *) expr)) - return PARTCLAUSE_UNSUPPORTED; - /* * See if the operator is relevant to the partitioning opfamily. * @@ -1945,7 +1987,7 @@ match_clause_to_partition_key(RelOptInfo *rel, /* Finally, generate steps */ *clause_steps = - gen_partprune_steps_internal(context, rel, elem_clauses, + gen_partprune_steps_internal(context, rel, pruneenv, elem_clauses, &contradictory); if (contradictory) return PARTCLAUSE_MATCH_CONTRADICT; @@ -2961,15 +3003,18 @@ analyze_partkey_exprs(PartitionedRelPruneInfo *pinfo, List *steps, { PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc); ListCell *lc2; + ListCell *lc3; int keyno; if (!IsA(step, PartitionPruneStepOp)) continue; keyno = 0; - foreach(lc2, step->exprs) + forboth(lc2, step->exprs, lc3, step->cmpfns) { Expr *expr = lfirst(lc2); + Oid fnoid = lfirst_oid(lc3); + if (!IsA(expr, Const)) { @@ -2992,6 +3037,12 @@ analyze_partkey_exprs(PartitionedRelPruneInfo *pinfo, List *steps, doruntimeprune = true; } + else if (func_volatile(fnoid) != PROVOLATILE_IMMUTABLE) + { + /* No need to check for exec params inside a Const */ + doruntimeprune = true; + pinfo->do_initial_prune = true; + } keyno++; } }