From da26ad7bd253fdfbc5cda9d304cc0da8a0f2734a Mon Sep 17 00:00:00 2001 From: David Rowley Date: Thu, 1 Mar 2018 23:02:26 +1300 Subject: [PATCH v14 3/5] Provide infrastructure to allow partition pruning during execution The query planner supports eliminating partitions of a partitioned table during query planning. This has its limitations as it can only perform the elimination using clauses which can be evaluated during planning. Allowing this partition elimination to occur during execution allows the values of Params to be used for elimination too, thus opening the door for PREPAREd statements to have unneeded partitions pruned too. The infrastructure provided here permits the building of a data structure which is able to perform the translation of the matching partition IDs as is returned by the existing partition pruning code into the List index of a subpaths list, as exist in node types such as Append, MergeAppend and ModifyTable. This allows us to translate a list of clauses into a Bitmapset of all the subpath indexes which must be included to satisfy the clause list. This commit does not add support for any node types. Support for this will arrive in follow-up commits. --- src/backend/executor/execPartition.c | 234 ++++++++++++++++++++++++++++ src/backend/nodes/copyfuncs.c | 19 +++ src/backend/nodes/outfuncs.c | 25 +++ src/backend/nodes/readfuncs.c | 18 +++ src/backend/optimizer/util/partprune.c | 269 ++++++++++++++++++++++++++++----- src/include/catalog/partition.h | 10 ++ src/include/executor/execPartition.h | 67 ++++++++ src/include/nodes/nodes.h | 1 + src/include/nodes/primnodes.h | 21 +++ src/include/optimizer/partprune.h | 4 + 10 files changed, 634 insertions(+), 34 deletions(-) diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 54efc9e..9a87245 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -19,6 +19,7 @@ #include "executor/executor.h" #include "mb/pg_wchar.h" #include "miscadmin.h" +#include "optimizer/partprune.h" #include "utils/lsyscache.h" #include "utils/rls.h" #include "utils/ruleutils.h" @@ -36,6 +37,9 @@ static char *ExecBuildSlotPartitionKeyDescription(Relation rel, Datum *values, bool *isnull, int maxfieldlen); +static void find_matching_subplans_recurse( + PartitionedRelPruning *partrelprune, + Bitmapset **validsubplans); /* * ExecSetupPartitionTupleRouting - sets up information needed during @@ -946,3 +950,233 @@ ExecBuildSlotPartitionKeyDescription(Relation rel, return buf.data; } + +/* + * ExecSetupPartitionPruning + * + * Setup the required data structure for calling ExecFindMatchingSubPlans. + * + * 'partitionpruneinfo' is a List of PartitionPruneInfos as generated by + * make_partition_pruneinfo. Here we pre-process the 'prunequal' of each + * PartitionPruneInfo and also extract any parameter IDs of Params which + * can be matched to the partition key. We also build a PartitionPruneContext + * for each item in the List. These are reusable and need only be setup once. + * + * It is possible that no params can be matched to for one, or even all + * partition parents. For the latter case, there is not much use in calling + * ExecFindMatchingSubPlans() on the return value of this function as it will + * always just return all possible subnodes. The param IDs of all parameters + * matching partition keys in the entire partition hierarchy are stored in + * the return value's 'prune_param_ids' field. Callers may wish to check + * that this is not an empty set before attempting to further narrow the list + * of subplans to scan. + */ +PartitionPruning * +ExecSetupPartitionPruning(PlanState *planstate, List *partitionpruneinfo) +{ + PartitionedRelPruning *partrelprunes; + PartitionPruning *partprune; + ListCell *lc; + int i; + + Assert(partitionpruneinfo != NIL); + + partprune = (PartitionPruning *) palloc(sizeof(PartitionPruning)); + partrelprunes = (PartitionedRelPruning *) + palloc(sizeof(PartitionedRelPruning) * + list_length(partitionpruneinfo)); + + /* + * The first item in the array contains the details for the query's target + * partition, so record that as the root of the partition hierarchy. + */ + partprune->partition_rel_pruning = &partrelprunes[0]; + partprune->prune_param_ids = NULL; + + /* + * Create a sub memory context which we'll use when making calls to the + * query planner function to determine which partitions will match. The + * planner is not too careful about freeing memory, so we'll ensure we + * call the function in this context to avoid any memory leaking in the + * executor's memory context. + */ + partprune->prune_context = AllocSetContextCreate(CurrentMemoryContext, + "Partition Prune", + ALLOCSET_DEFAULT_SIZES); + + i = 0; + foreach(lc, partitionpruneinfo) + { + PartitionPruneInfo *pinfo = (PartitionPruneInfo *) lfirst(lc); + PartitionedRelPruning *partrelprune = &partrelprunes[i]; + PartitionPruneContext *context = &partrelprune->context; + PartitionDesc partdesc; + Relation rel; + PartitionKey partkey; + List **partexprs; + int partnatts; + int j; + + partrelprune->relid = pinfo->relid; + partrelprune->allsubnodes = pinfo->allsubnodes; + partrelprune->nparts = pinfo->nparts; + partrelprune->subnodeindex = pinfo->subnodeindex; + partrelprune->subpartprune = palloc(sizeof(PartitionedRelPruning *) * + pinfo->nparts); + + for (j = 0; j < pinfo->nparts; j++) + { + int subpartidx = pinfo->subpartindex[j]; + + Assert(subpartidx < list_length(partitionpruneinfo)); + + if (subpartidx >= 0) + partrelprune->subpartprune[j] = &partrelprunes[subpartidx]; + else + partrelprune->subpartprune[j] = NULL; + } + + rel = relation_open(pinfo->reloid, NoLock); + + partkey = RelationGetPartitionKey(rel); + partdesc = RelationGetPartitionDesc(rel); + + context->relid = pinfo->relid; + context->strategy = partkey->strategy; + context->partnatts = partnatts = partkey->partnatts; + partexprs = build_partition_expressions(partkey, pinfo->relid); + context->partkeys = (Expr **) palloc(sizeof(Expr *) * partnatts); + + for (j = 0; j < partnatts; j++) + context->partkeys[j] = (Expr *) linitial(partexprs[j]); + + context->partopcintype = partkey->partopcintype; + context->partopfamily = partkey->partopfamily; + context->partcollation = partkey->partcollation; + context->partsupfunc = partkey->partsupfunc; + context->nparts = pinfo->nparts; + context->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey); + + if (OidIsValid(get_default_oid_from_partdesc(partdesc))) + context->has_default_part = true; + else + context->has_default_part = false; + + context->partition_qual = RelationGetPartitionQual(rel); + + context->planstate = planstate; + context->paramids = NULL; + + partrelprune->partclauseinfo = generate_partition_clauses(context, + pinfo->prunequal); + + partprune->prune_param_ids = + bms_add_members(partprune->prune_param_ids, + context->paramids); + + relation_close(rel, NoLock); + + i++; + } + + return partprune; +} + +/* + * ExecFindMatchingSubPlans + * Determine which subset of subplan nodes we need to scan based on the + * details stored in 'partprune'. All subplans which provably cannot + * possibly have matching records are eliminated and the indexes of the + * remaining set are returned in the form of a Bitmapset. + */ +Bitmapset * +ExecFindMatchingSubPlans(PartitionPruning *partprune) +{ + PartitionedRelPruning *partrelprune; + MemoryContext oldcontext; + Bitmapset *result = NULL; + + partrelprune = partprune->partition_rel_pruning; + + /* + * Switch to a temp context to avoid leaking memory in the + * executor's memory context. + */ + oldcontext = MemoryContextSwitchTo(partprune->prune_context); + + find_matching_subplans_recurse(partrelprune, &result); + + MemoryContextSwitchTo(oldcontext); + + /* Move to the correct memory context */ + result = bms_copy(result); + + MemoryContextReset(partprune->prune_context); + + return result; +} + +/* + * find_matching_subplans_recurse + * Recursive worker function for ExecFindMatchingSubPlans. + */ +static void +find_matching_subplans_recurse(PartitionedRelPruning *partrelprune, + Bitmapset **validsubplans) +{ + PartitionPruneContext *context = &partrelprune->context; + PartitionClauseInfo *partclauseinfo; + Bitmapset *partset; + int i; + + check_stack_depth(); + + partclauseinfo = partrelprune->partclauseinfo; + + /* + * Detect if any impossibilities were discovered during + * generate_partition_clauses + */ + if (partclauseinfo->constfalse) + { + bms_free(*validsubplans); + *validsubplans = NULL; + return; + } + + /* + * We only need to determine the matching partitions if there are any + * params matching the partition key at this level. If there are no + * matching params, then we can simply return all subnodes which belong + * to this parent partition. The planner should have already determined + * these to be the minimum possible set. We must still recursively visit + * any subpartitions as we may find their partitions keys match some + * params. + */ + if (!bms_is_empty(context->paramids)) + partset = get_partitions_from_clauses(context, partclauseinfo); + else + partset = partrelprune->allsubnodes; + + /* Translate partset into subnode indexes */ + i = -1; + while ((i = bms_next_member(partset, i)) >= 0) + { + if (partrelprune->subnodeindex[i] >= 0) + *validsubplans = bms_add_member(*validsubplans, + partrelprune->subnodeindex[i]); + else if (partrelprune->subpartprune[i] != NULL) + find_matching_subplans_recurse(partrelprune->subpartprune[i], + validsubplans); + else + { + /* + * If this happens then we're somehow missing a subnode. This + * shouldn't happen and could only happen if a more restrictive + * clause list was used for partition elimination during planning + * than what was used here. + */ + elog(ERROR, "partition missing from subplans"); + } + } +} \ No newline at end of file diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 169c697..2d6b943 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2132,6 +2132,22 @@ _copyOnConflictExpr(const OnConflictExpr *from) return newnode; } +static PartitionPruneInfo * +_copyPartitionPruneInfo(const PartitionPruneInfo *from) +{ + PartitionPruneInfo *newnode = makeNode(PartitionPruneInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(reloid); + COPY_NODE_FIELD(prunequal); + COPY_BITMAPSET_FIELD(allsubnodes); + COPY_SCALAR_FIELD(nparts); + COPY_POINTER_FIELD(subnodeindex, from->nparts * sizeof(int)); + COPY_POINTER_FIELD(subpartindex, from->nparts * sizeof(int)); + + return newnode; +} + /* **************************************************************** * relation.h copy functions * @@ -5028,6 +5044,9 @@ copyObjectImpl(const void *from) case T_PlaceHolderInfo: retval = _copyPlaceHolderInfo(from); break; + case T_PartitionPruneInfo: + retval = _copyPartitionPruneInfo(from); + break; /* * VALUE NODES diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index fe309a6..6fd547c 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1707,6 +1707,28 @@ _outOnConflictExpr(StringInfo str, const OnConflictExpr *node) WRITE_NODE_FIELD(exclRelTlist); } +static void +_outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) +{ + int i; + + WRITE_NODE_TYPE("PARTITIONPRUNEINFO"); + + WRITE_INT_FIELD(relid); + WRITE_OID_FIELD(reloid); + WRITE_NODE_FIELD(prunequal); + WRITE_BITMAPSET_FIELD(allsubnodes); + WRITE_INT_FIELD(nparts); + + appendStringInfoString(str, " :subnodeindex"); + for (i = 0; i < node->nparts; i++) + appendStringInfo(str, " %d", node->subnodeindex[i]); + + appendStringInfoString(str, " :subpartindex"); + for (i = 0; i < node->nparts; i++) + appendStringInfo(str, " %d", node->subpartindex[i]); +} + /***************************************************************************** * * Stuff from relation.h. @@ -3922,6 +3944,9 @@ outNode(StringInfo str, const void *obj) case T_OnConflictExpr: _outOnConflictExpr(str, obj); break; + case T_PartitionPruneInfo: + _outPartitionPruneInfo(str, obj); + break; case T_Path: _outPath(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 068db35..7bb92d4 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1328,6 +1328,22 @@ _readOnConflictExpr(void) READ_DONE(); } +static PartitionPruneInfo * +_readPartitionPruneInfo(void) +{ + READ_LOCALS(PartitionPruneInfo); + + READ_INT_FIELD(relid); + READ_OID_FIELD(reloid); + READ_NODE_FIELD(prunequal); + READ_BITMAPSET_FIELD(allsubnodes); + READ_INT_FIELD(nparts); + READ_INT_ARRAY(subnodeindex, local_node->nparts); + READ_INT_ARRAY(subpartindex, local_node->nparts); + + READ_DONE(); +} + /* * Stuff from parsenodes.h. */ @@ -2571,6 +2587,8 @@ parseNodeString(void) return_value = _readFromExpr(); else if (MATCH("ONCONFLICTEXPR", 14)) return_value = _readOnConflictExpr(); + else if (MATCH("PARTITIONPRUNEINFO", 18)) + return_value = _readPartitionPruneInfo(); else if (MATCH("RTE", 3)) return_value = _readRangeTblEntry(); else if (MATCH("RANGETBLFUNCTION", 16)) diff --git a/src/backend/optimizer/util/partprune.c b/src/backend/optimizer/util/partprune.c index 70a215f..096f2d4 100644 --- a/src/backend/optimizer/util/partprune.c +++ b/src/backend/optimizer/util/partprune.c @@ -38,6 +38,11 @@ * while also taking into account strategies of the operators in the matched * clauses. * + * make_partition_pruneinfo() + * + * Generates a List of PartitionPruneInfo nodes for use in the executor to + * allow it to perform partition pruning during execution. + * * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * @@ -53,10 +58,12 @@ #include "catalog/pg_operator.h" #include "catalog/pg_opfamily.h" #include "catalog/pg_type.h" +#include "executor/executor.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/partprune.h" +#include "optimizer/pathnode.h" #include "optimizer/planner.h" #include "optimizer/predtest.h" #include "optimizer/prep.h" @@ -104,16 +111,16 @@ static Bitmapset *get_partitions_from_or_args(PartitionPruneContext *context, static void remove_redundant_clauses(PartitionPruneContext *context, PartitionClauseInfo *partclauseinfo, List **minimalclauses); -static bool partition_cmp_args(Oid partopcintype, Oid partopfamily, - PartClause *pc, PartClause *leftarg, PartClause *rightarg, - bool *result); +static bool partition_cmp_args(PartitionPruneContext *context, + Oid partopcintype, Oid partopfamily, PartClause *pc, + PartClause *leftarg, PartClause *rightarg, bool *result); static bool extract_bounding_datums(PartitionPruneContext *context, PartitionClauseInfo *clauseinfo, List **minimalclauses, PartScanKeyInfo *keys); static PartOpStrategy partition_op_strategy(char part_strategy, PartClause *pc, bool *incl); -static bool partkey_datum_from_expr(Oid partopcintype, Expr *expr, - Datum *value); +static bool partkey_datum_from_expr(PartitionPruneContext *context, + Oid partopcintype, Expr *expr, Datum *value); /* * prune_append_rel_partitions @@ -158,6 +165,8 @@ prune_append_rel_partitions(PlannerInfo *root, RelOptInfo *rel) context.boundinfo = rel->boundinfo; context.has_default_part = rel->has_default_part; context.partition_qual = rel->partition_qual; + context.planstate = NULL; + context.paramids = NULL; /* process clauses */ partclauseinfo = generate_partition_clauses(&context, clauses); @@ -534,6 +543,10 @@ extract_partition_clauses(PartitionPruneContext *context, List *clauses) pc->inputcollid = opclause->inputcollid; pc->value = valueexpr; + if (IsA(valueexpr, Param)) + context->paramids = bms_add_member(context->paramids, + ((Param *) valueexpr)->paramid); + /* * We don't turn a <> operator clause into a key right away. * Instead, the caller will hand over such clauses to @@ -695,6 +708,11 @@ extract_partition_clauses(PartitionPruneContext *context, List *clauses) leftop, rightop, InvalidOid, saop_coll); + + if (IsA(rightop, Param)) + context->paramids = bms_add_member(context->paramids, + ((Param *) rightop)->paramid); + elem_clauses = lappend(elem_clauses, elem_clause); } @@ -966,7 +984,8 @@ remove_redundant_clauses(PartitionPruneContext *context, if (hash_clause == NULL) hash_clause = pc; /* check if another clause would contradict the one we have */ - else if (partition_cmp_args(context->partopcintype[i], + else if (partition_cmp_args(context, + context->partopcintype[i], context->partopfamily[i], pc, pc, hash_clause, &test_result)) @@ -1023,7 +1042,8 @@ remove_redundant_clauses(PartitionPruneContext *context, * then because 7 < 5 is false, we leave a < 5 where it is and * effectively discard a < 7 as being redundant. */ - if (partition_cmp_args(context->partopcintype[i], + if (partition_cmp_args(context, + context->partopcintype[i], context->partopfamily[i], pc, pc, btree_clauses[s], &test_result)) @@ -1080,7 +1100,8 @@ remove_redundant_clauses(PartitionPruneContext *context, * eq clause is a = 3, then because 3 < 5, we no longer need * a < 5, because a = 3 is more restrictive. */ - if (partition_cmp_args(context->partopcintype[i], + if (partition_cmp_args(context, + context->partopcintype[i], context->partopfamily[i], chk, eq, chk, &test_result)) @@ -1111,7 +1132,8 @@ remove_redundant_clauses(PartitionPruneContext *context, PartClause *lt = btree_clauses[BTLessStrategyNumber - 1], *le = btree_clauses[BTLessEqualStrategyNumber - 1]; - if (partition_cmp_args(context->partopcintype[i], + if (partition_cmp_args(context, + context->partopcintype[i], context->partopfamily[i], le, lt, le, &test_result)) @@ -1130,7 +1152,8 @@ remove_redundant_clauses(PartitionPruneContext *context, PartClause *gt = btree_clauses[BTGreaterStrategyNumber - 1], *ge = btree_clauses[BTGreaterEqualStrategyNumber - 1]; - if (partition_cmp_args(context->partopcintype[i], + if (partition_cmp_args(context, + context->partopcintype[i], context->partopfamily[i], ge, gt, ge, &test_result)) @@ -1168,9 +1191,9 @@ remove_redundant_clauses(PartitionPruneContext *context, * incompatible with the operator. */ static bool -partition_cmp_args(Oid partopcintype, Oid partopfamily, - PartClause *pc, PartClause *leftarg, PartClause *rightarg, - bool *result) +partition_cmp_args(PartitionPruneContext *context, Oid partopcintype, + Oid partopfamily, PartClause *pc, PartClause *leftarg, + PartClause *rightarg, bool *result) { Datum left_value; Datum right_value; @@ -1181,10 +1204,12 @@ partition_cmp_args(Oid partopcintype, Oid partopfamily, * Try to extract an actual value from each arg. This may fail if the * value is unknown in this context, in which case we cannot compare. */ - if (!partkey_datum_from_expr(partopcintype, leftarg->value, &left_value)) + if (!partkey_datum_from_expr(context, partopcintype, leftarg->value, + &left_value)) return false; - if (!partkey_datum_from_expr(partopcintype, rightarg->value, &right_value)) + if (!partkey_datum_from_expr(context, partopcintype, rightarg->value, + &right_value)) return false; /* @@ -1308,12 +1333,14 @@ extract_bounding_datums(PartitionPruneContext *context, case PART_OP_EQUAL: Assert(incl); if (need_next_eq && - partkey_datum_from_expr(context->partopcintype[i], + partkey_datum_from_expr(context, + context->partopcintype[i], value, &keys->eqkeys[i])) keys->n_eqkeys++; if (need_next_max && - partkey_datum_from_expr(context->partopcintype[i], + partkey_datum_from_expr(context, + context->partopcintype[i], value, &keys->maxkeys[i])) { keys->n_maxkeys++; @@ -1321,7 +1348,8 @@ extract_bounding_datums(PartitionPruneContext *context, } if (need_next_min && - partkey_datum_from_expr(context->partopcintype[i], + partkey_datum_from_expr(context, + context->partopcintype[i], value, &keys->minkeys[i])) { keys->n_minkeys++; @@ -1331,7 +1359,8 @@ extract_bounding_datums(PartitionPruneContext *context, case PART_OP_LESS: if (need_next_max && - partkey_datum_from_expr(context->partopcintype[i], + partkey_datum_from_expr(context, + context->partopcintype[i], value, &keys->maxkeys[i])) { keys->n_maxkeys++; @@ -1343,7 +1372,8 @@ extract_bounding_datums(PartitionPruneContext *context, case PART_OP_GREATER: if (need_next_min && - partkey_datum_from_expr(context->partopcintype[i], + partkey_datum_from_expr(context, + context->partopcintype[i], value, &keys->minkeys[i])) { keys->n_minkeys++; @@ -1388,7 +1418,8 @@ extract_bounding_datums(PartitionPruneContext *context, PartClause *pc = (PartClause *) lfirst(lc); Datum datum; - if (partkey_datum_from_expr(context->partopcintype[0], + if (partkey_datum_from_expr(context, + context->partopcintype[0], pc->value, &datum)) keys->ne_datums[i++] = datum; } @@ -1469,7 +1500,8 @@ partition_op_strategy(char part_strategy, PartClause *pc, bool *incl) * set. True is returned otherwise. */ static bool -partkey_datum_from_expr(Oid partopcintype, Expr *expr, Datum *value) +partkey_datum_from_expr(PartitionPruneContext *context, Oid partopcintype, + Expr *expr, Datum *value) { Oid exprtype = exprType((Node *) expr); @@ -1493,25 +1525,194 @@ partkey_datum_from_expr(Oid partopcintype, Expr *expr, Datum *value) if (expr == NULL) return false; - /* - * Transform into a form that the following code can do something - * useful with. - */ - expr = evaluate_expr(expr, - exprType((Node *) expr), - exprTypmod((Node *) expr), - exprCollation((Node *) expr)); + if (context->planstate) + { + ExprState *exprstate; + bool isNull; + + exprstate = ExecInitExpr(expr, context->planstate); + + *value = ExecEvalExprSwitchContext(exprstate, + context->planstate->ps_ExprContext, + &isNull); + + if (isNull) + return false; + + return true; + } + else + { + /* + * Transform into a form that the following code can do something + * useful with. + */ + expr = evaluate_expr(expr, + exprType((Node *) expr), + exprTypmod((Node *) expr), + exprCollation((Node *) expr)); + } } /* * Add more expression types here as needed to support the requirements * of the higher-level code. */ - if (IsA(expr, Const)) + switch (nodeTag(expr)) { - *value = ((Const *) expr)->constvalue; - return true; + case T_Const: + *value = ((Const *) expr)->constvalue; + return true; + + case T_Param: + if (context->planstate) + { + ExprState *exprstate; + bool isNull; + + exprstate = ExecInitExpr(expr, context->planstate); + *value = ExecEvalExprSwitchContext(exprstate, + context->planstate->ps_ExprContext, + &isNull); + + if (isNull) + return false; + + return true; + } + + default: + return false; } +} - return false; +/* + * make_partition_pruneinfo + * Build a List of PartitionPruneInfos, one for each 'partitioned_rel'. + * + * Here we index the subpaths by partition index so that we're able to + * translate the output of get_partitions_from_clauses into subpath indexes + * to possibly allow for further partition pruning to be performed during + * execution. + */ +List * +make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, + List *partition_rels, List *subpaths, + List *prunequal) +{ + RangeTblEntry *rte; + ListCell *lc; + List *pinfolist = NIL; + int *allsubnodeindex; + int *allsubpartindex; + int i; + + Assert(parentrel->reloptkind == RELOPT_BASEREL); + + /* + * Allocate two arrays, one to allow quick lookups of the 'subpaths' index + * of a relation by relid and another to lookup the 'partitioned_rel' + * index by relid. + */ + allsubnodeindex = palloc(sizeof(int) * root->simple_rel_array_size); + allsubpartindex = palloc(sizeof(int) * root->simple_rel_array_size); + + /* Initialize to -1 to indicate the rel was not found */ + for (i = 0; i < root->simple_rel_array_size; i++) + { + allsubnodeindex[i] = -1; + allsubpartindex[i] = -1; + } + + /* + * Now loop over each subpath and fill in the index of the subpath for the + * subpath's relid. + */ + i = 0; + foreach(lc, subpaths) + { + Path *path = (Path *) lfirst(lc); + RelOptInfo *pathrel = path->parent; + + Assert(IS_SIMPLE_REL(pathrel)); + Assert(pathrel->relid < root->simple_rel_array_size); + + allsubnodeindex[pathrel->relid] = i; + i++; + } + + /* Likewise for the partition_rels */ + i = 0; + foreach(lc, partition_rels) + { + Index rti = lfirst_int(lc); + + Assert(rti < root->simple_rel_array_size); + + allsubpartindex[rti] = i; + i++; + } + + /* We now build a PartitionPruneInfo for each partition_rels */ + foreach(lc, partition_rels) + { + Index rti = lfirst_int(lc); + RelOptInfo *subpart = find_base_rel(root, rti); + PartitionPruneInfo *pinfo; + int nparts = subpart->nparts; + int *subnodeindex; + int *subpartindex; + + rte = root->simple_rte_array[subpart->relid]; + + pinfo = makeNode(PartitionPruneInfo); + pinfo->relid = subpart->relid; + pinfo->reloid = rte->relid; + + /* prunequal will only require translation for sub-partitions */ + if (subpart == parentrel) + pinfo->prunequal = prunequal; + else + pinfo->prunequal = (List *) + adjust_appendrel_attrs_multilevel(root, + (Node *) prunequal, + subpart->relids, + parentrel->relids); + pinfo->allsubnodes = NULL; + pinfo->nparts = nparts; + pinfo->subnodeindex = subnodeindex = palloc(nparts * sizeof(int)); + pinfo->subpartindex = subpartindex = palloc(nparts * sizeof(int)); + + /* + * Loop over each partition of the partitioned rel and record the + * subpath index for each. Any partitions which are not present + * in the subpaths List will be set to -1, and any subpartitioned + * table which is not present will also be set to -1. + */ + for (i = 0; i < nparts; i++) + { + RelOptInfo *partrel = subpart->part_rels[i]; + int subnodeidx = allsubnodeindex[partrel->relid]; + int subpartidx = allsubpartindex[partrel->relid]; + + subnodeindex[i] = subnodeidx; + subpartindex[i] = subpartidx; + + /* + * Record the indexes of all the partition indexes that we have + * subnodes or subparts for. This allows an optimization to skip + * attempting any run-time pruning when no Params are found + * matching the partition key. + */ + if (subnodeidx >= 0 || subpartidx >= 0) + pinfo->allsubnodes = bms_add_member(pinfo->allsubnodes, i); + } + + pinfolist = lappend(pinfolist, pinfo); + } + + pfree(allsubnodeindex); + pfree(allsubpartindex); + + return pinfolist; } diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index 8a487e0..88b934e 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -16,6 +16,7 @@ #include "fmgr.h" #include "executor/tuptable.h" #include "nodes/execnodes.h" +#include "nodes/relation.h" #include "parser/parse_node.h" #include "utils/rel.h" @@ -67,6 +68,15 @@ typedef struct PartitionPruneContext /* Partition boundary info */ PartitionBoundInfo boundinfo; + + /* + * Can be set when the context is used from the executor to allow + * resolution of Param values. + */ + PlanState *planstate; + + /* ParamIds of clauses matching the partition key */ + Bitmapset *paramids; } PartitionPruneContext; /* diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h index 03a599a..6fdd514 100644 --- a/src/include/executor/execPartition.h +++ b/src/include/executor/execPartition.h @@ -17,6 +17,7 @@ #include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "nodes/plannodes.h" +#include "optimizer/partprune.h" /*----------------------- * PartitionDispatch - information about one partitioned table in a partition @@ -108,6 +109,69 @@ typedef struct PartitionTupleRouting TupleTableSlot *root_tuple_slot; } PartitionTupleRouting; +/*----------------------- + * PartitionedRelPruning - Encapsulates all information required to support + * elimination of partitions in node types which support arbitrary Lists of + * subplans. Information stored here allows partprune.c's partition pruning + * functions to be called and the return value of partition indexes translated + * into the subpath indexes of node types such as Append, thus allowing us to + * bypass certain subnodes when we have proofs that indicate that no tuple + * matching the quals stored in 'partclauseinfo' will be found within. + * + * relid Relation index of the partitioned table that + * this information belongs to. + * nparts The number of partitions which belong to this + * partitioned relation. Also defines the size of + * the 'subnodeindex' and 'subpartprune' arrays. + * subnodeindex An array of nparts containing the subnode + * index which matches this partition index, or + * -1 if there is no match. + * subpartprune An array of nparts containing the + * PartitionedRelPruning details this partition + * index for sub-partitioned tables. + * allsubnodes A Bitmapset of all subnode indexes which + * belong to this partition. + * context Contains the context details required to call + * the partition pruning code. + * partclauseinfo Contains clauses pre-matched to the partition + * key ready to be used for partition pruning. + *----------------------- + */ +typedef struct PartitionedRelPruning +{ + int relid; + int nparts; + int *subnodeindex; + struct PartitionedRelPruning **subpartprune; + Bitmapset *allsubnodes; + PartitionPruneContext context; + PartitionClauseInfo *partclauseinfo; +} PartitionedRelPruning; + +/*----------------------- + * PartitionPruning - Encapsulates a hierarchy of PartitionedRelPruning + * structs and also stores all Param IDs which were found to match the + * partition keys of each partition. This struct can be attached to node + * types which support arbitrary Lists of subnodes containing partitions to + * allow subnodes to be eliminated due to the clauses being unable to match + * to any tuple that the subnode could possibly produce. + * + * partition_rel_pruning PartitionedRelPruning for the node's target + * partitioned relation. + * prune_param_ids All Param IDs which were found to match a + * partition key in each of the contained + * PartitionedRelPruning structs. + * prune_context A memory context which can be used to call + * the query planner's partition prune functions. + *----------------------- + */ +typedef struct PartitionPruning +{ + PartitionedRelPruning *partition_rel_pruning; + Bitmapset *prune_param_ids; + MemoryContext prune_context; +} PartitionPruning; + extern PartitionTupleRouting *ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel); extern int ExecFindPartition(ResultRelInfo *resultRelInfo, @@ -126,5 +190,8 @@ extern HeapTuple ConvertPartitionTupleSlot(TupleConversionMap *map, TupleTableSlot *new_slot, TupleTableSlot **p_my_slot); extern void ExecCleanupTupleRouting(PartitionTupleRouting *proute); +extern PartitionPruning *ExecSetupPartitionPruning(PlanState *planstate, + List *partitionpruneinfo); +extern Bitmapset *ExecFindMatchingSubPlans(PartitionPruning *partprune); #endif /* EXECPARTITION_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index c097da6..d693f37 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -191,6 +191,7 @@ typedef enum NodeTag T_FromExpr, T_OnConflictExpr, T_IntoClause, + T_PartitionPruneInfo, /* * TAGS FOR EXPRESSION STATE NODES (execnodes.h) diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 1b4b0d7..bbf3c68 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1506,4 +1506,25 @@ typedef struct OnConflictExpr List *exclRelTlist; /* tlist of the EXCLUDED pseudo relation */ } OnConflictExpr; +/*---------- + * PartitionPruneInfo - Details required to allow the executor to prune + * partitions. + * + * Here we store mapping details to allow translation of a partitioned table's + * index into subnode indexes for node types which support arbitrary numbers + * of sub nodes, such as Append. + *---------- + */ +typedef struct PartitionPruneInfo +{ + NodeTag type; + int relid; /* relation index of parent partition rel */ + Oid reloid; /* Oid of partition rel */ + List *prunequal; /* qual list for pruning partitions */ + Bitmapset *allsubnodes; /* All subnode indexes at this level */ + int nparts; /* length of the following arrays */ + int *subnodeindex; /* subnode index indexed by partition id */ + int *subpartindex; /* subpart index indexed by partition id */ +} PartitionPruneInfo; + #endif /* PRIMNODES_H */ diff --git a/src/include/optimizer/partprune.h b/src/include/optimizer/partprune.h index b654691..ec96da4 100644 --- a/src/include/optimizer/partprune.h +++ b/src/include/optimizer/partprune.h @@ -68,4 +68,8 @@ extern PartitionClauseInfo *generate_partition_clauses( extern Bitmapset *get_partitions_from_clauses(PartitionPruneContext *context, PartitionClauseInfo *partclauseinfo); +extern List *make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, + List *partition_rels, List *subpaths, + List *prunequal); + #endif /* PARTPRUNE_H */ -- 1.9.5.msysgit.1