From 6972ae859d2543225baf669363bfe6f8e70c0add Mon Sep 17 00:00:00 2001 From: houzj Date: Mon, 17 May 2021 17:43:57 +0800 Subject: [PATCH] skip-tuple-routing-for-constant-partition-key --- src/backend/access/common/tupconvert.c | 37 +++++++++++++++ src/backend/commands/copyfrom.c | 3 ++ src/backend/executor/execPartition.c | 84 ++++++++++++++++++++++++++++++++-- src/backend/executor/nodeModifyTable.c | 51 ++++++++++++++++++++- src/include/access/tupconvert.h | 1 + src/include/nodes/execnodes.h | 9 ++++ 6 files changed, 179 insertions(+), 6 deletions(-) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 64f5439..f22414f 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -278,6 +278,43 @@ execute_attr_map_cols(AttrMap *attrMap, Bitmapset *in_cols) } /* + * Perform conversion of bitmap of columns according to the map. + * + * Only convert normal user column. + * + * output column that does not correspond to any input column will + * still be set in bitmap. + */ +Bitmapset * +execute_attr_map_cols_with_null(AttrMap *attrMap, Bitmapset *in_cols) +{ + Bitmapset *out_cols; + int out_attnum; + + /* fast path for the common trivial case */ + if (in_cols == NULL) + return NULL; + + out_cols = NULL; + + for (out_attnum = 1; + out_attnum <= attrMap->maplen; + out_attnum++) + { + int in_attnum; + + in_attnum = attrMap->attnums[out_attnum - 1]; + + if (in_attnum == 0 || + bms_is_member(in_attnum, in_cols)) + out_cols = bms_add_member(out_cols, out_attnum); + } + + return out_cols; +} + + +/* * Free a TupleConversionMap structure. */ void diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 40a54ad..ea62cfa 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -670,6 +670,9 @@ CopyFrom(CopyFromState cstate) mtstate->mt_nrels = 1; mtstate->resultRelInfo = resultRelInfo; mtstate->rootResultRelInfo = resultRelInfo; + mtstate->const_bms = NULL; + mtstate->cache_routed_rel = false; + mtstate->targetpartrel = NULL; if (resultRelInfo->ri_FdwRoutine != NULL && resultRelInfo->ri_FdwRoutine->BeginForeignInsert != NULL) diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 606c920..dca6799 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -24,6 +24,7 @@ #include "mb/pg_wchar.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "optimizer/optimizer.h" #include "partitioning/partbounds.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" @@ -191,7 +192,7 @@ static void find_matching_subplans_recurse(PartitionPruningData *prunedata, PartitionedRelPruningData *pprune, bool initial_prune, Bitmapset **validsubplans); - +static bool ExecSimplifyTupleRouting(PartitionDispatch pd, Bitmapset *constcols); /* * ExecSetupPartitionTupleRouting - sets up information needed during @@ -271,6 +272,15 @@ ExecFindPartition(ModifyTableState *mtstate, TupleTableSlot *myslot = NULL; MemoryContext oldcxt; ResultRelInfo *rri = NULL; + bool need_tuple_routing; + Bitmapset *const_bms = mtstate->const_bms; + Bitmapset *root_const_bms = mtstate->const_bms; + + need_tuple_routing = !(RelationGetRelid(rootResultRelInfo->ri_RelationDesc) == + RelationGetRelid(mtstate->rootResultRelInfo->ri_RelationDesc)); + + if (mtstate->targetpartrel != NULL && !need_tuple_routing) + return mtstate->targetpartrel; /* use per-tuple context here to avoid leaking memory */ oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); @@ -327,6 +337,11 @@ ExecFindPartition(ModifyTableState *mtstate, errtable(rel))); } + /* Check if we can skip tuple routing next time */ + if (mtstate->cache_routed_rel) + mtstate->cache_routed_rel = ExecSimplifyTupleRouting(dispatch, + const_bms); + is_leaf = partdesc->is_leaf[partidx]; if (is_leaf) { @@ -415,8 +430,8 @@ ExecFindPartition(ModifyTableState *mtstate, } /* - * Convert the tuple to the new parent's layout, if different from - * the previous parent. + * Convert the tuple and constant value bitmap to the new parent's + * layout, if different from the previous parent. */ if (dispatch->tupslot) { @@ -424,6 +439,10 @@ ExecFindPartition(ModifyTableState *mtstate, TupleTableSlot *tempslot = myslot; myslot = dispatch->tupslot; + + if (mtstate->cache_routed_rel) + const_bms = execute_attr_map_cols_with_null(map, const_bms); + slot = execute_attr_map_slot(map, slot, myslot); if (tempslot != NULL) @@ -450,24 +469,43 @@ ExecFindPartition(ModifyTableState *mtstate, * * Note that we have a map to convert from root to current * partition, but not from immediate parent to current partition. - * So if we have to convert, do it from the root slot; if not, use - * the root slot as-is. + * So if we have to convert, do it from the root slot and constant + * value bitmap; if not, use the root slot as-is. */ if (is_leaf) { TupleConversionMap *map = rri->ri_RootToPartitionMap; if (map) + { + if (mtstate->cache_routed_rel) + const_bms = execute_attr_map_cols_with_null(map->attrMap, + const_bms); + slot = execute_attr_map_slot(map->attrMap, rootslot, rri->ri_PartitionTupleSlot); + } else + { + const_bms = root_const_bms; slot = rootslot; + } } ExecPartitionCheck(rri, slot, estate, true); } } + /* + * If all of the columns used in partition key are constant, cache the + * target partition if first time reach here. + */ + if (mtstate->cache_routed_rel) + { + mtstate->cache_routed_rel = false; + mtstate->targetpartrel = rri; + } + /* Release the tuple in the lowest parent's dedicated slot. */ if (myslot != NULL) ExecClearTuple(myslot); @@ -1165,6 +1203,42 @@ ExecCleanupTupleRouting(ModifyTableState *mtstate, } } +/* + * Check whether all of the columns used in partition key are + * const value + */ +static bool +ExecSimplifyTupleRouting(PartitionDispatch pd, + Bitmapset *constcols) +{ + int i; + List *expr_vars; + ListCell *lc; + + if (constcols == NULL) + return false; + + /* Check plain columns */ + for (i = 0; i < pd->key->partnatts; i++) + { + AttrNumber keycol = pd->key->partattrs[i]; + if (keycol != 0 && !bms_is_member(keycol, constcols)) + return false; + } + + /* Check columns in partition expression */ + expr_vars = pull_var_clause((Node *) pd->key->partexprs, 0); + foreach(lc, expr_vars) + { + Var *var = lfirst(lc); + + if (var->varno == 1 && !bms_is_member(var->varattno, constcols)) + return false; + } + + return true; +} + /* ---------------- * FormPartitionKeyDatum * Construct values[] and isnull[] arrays for the partition key diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 0816027..5d7dcb2 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1445,7 +1445,11 @@ ExecCrossPartitionUpdate(ModifyTableState *mtstate, /* Initialize tuple routing info if not already done. */ if (mtstate->mt_partition_tuple_routing == NULL) { + ListCell *lc, *lc2; + List *updateColnos; + List *targetlist; Relation rootRel = mtstate->rootResultRelInfo->ri_RelationDesc; + ModifyTable *node = (ModifyTable *) mtstate->ps.plan; MemoryContext oldcxt; /* Things built here have to last for the query duration. */ @@ -1454,6 +1458,22 @@ ExecCrossPartitionUpdate(ModifyTableState *mtstate, mtstate->mt_partition_tuple_routing = ExecSetupPartitionTupleRouting(estate, rootRel); + /* Initialize constant value bitmapset */ + Assert(mtstate->const_bms == NULL); + mtstate->cache_routed_rel = true; + updateColnos = (List *) list_nth(node->updateColnosLists, + resultRelInfo - mtstate->resultRelInfo); + + targetlist = outerPlan(node)->targetlist; + forboth(lc, targetlist, lc2, updateColnos) + { + TargetEntry *tle = lfirst_node(TargetEntry, lc); + AttrNumber targetattnum = lfirst_int(lc2); + if (!tle->resjunk && IsA(tle->expr, Const)) + mtstate->const_bms = bms_add_member(mtstate->const_bms, + targetattnum); + } + /* * Before a partition's tuple can be re-routed, it must first be * converted to the root's format, so we'll need a slot for storing @@ -1532,10 +1552,17 @@ ExecCrossPartitionUpdate(ModifyTableState *mtstate, */ tupconv_map = ExecGetChildToRootMap(resultRelInfo); if (tupconv_map != NULL) - slot = execute_attr_map_slot(tupconv_map->attrMap, + { + AttrMap *attrMap = tupconv_map->attrMap; + slot = execute_attr_map_slot(attrMap, slot, mtstate->mt_root_tuple_slot); + if (mtstate->cache_routed_rel) + mtstate->const_bms = execute_attr_map_cols_with_null(attrMap, + mtstate->const_bms); + } + /* Tuple routing starts from the root table. */ *inserted_tuple = ExecInsert(mtstate, mtstate->rootResultRelInfo, slot, planSlot, estate, canSetTag); @@ -2874,6 +2901,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) /* Get the root target relation */ rel = mtstate->rootResultRelInfo->ri_RelationDesc; + mtstate->cache_routed_rel = false; + mtstate->const_bms = NULL; + mtstate->targetpartrel = NULL; + /* * Build state for tuple routing if it's a partitioned INSERT. An UPDATE * might need this too, but only if it actually moves tuples between @@ -2881,9 +2912,27 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) */ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && operation == CMD_INSERT) + { mtstate->mt_partition_tuple_routing = ExecSetupPartitionTupleRouting(estate, rel); + mtstate->cache_routed_rel = true; + + /* + * Build the constant bitmap for tuple routing simplification if + * it's a partitioned INSERT. Cross partition UPDATE will build it + * in ExecCrossPartitionUpdate. + */ + i = 1; + foreach(l, subplan->targetlist) + { + TargetEntry *tc = (TargetEntry *) lfirst(l); + if (!tc->resjunk && IsA(tc->expr, Const)) + mtstate->const_bms = bms_add_member(mtstate->const_bms, i); + i++; + } + } + /* * Initialize any WITH CHECK OPTION constraints if needed. */ diff --git a/src/include/access/tupconvert.h b/src/include/access/tupconvert.h index a2cc4b3..ea8eeac 100644 --- a/src/include/access/tupconvert.h +++ b/src/include/access/tupconvert.h @@ -45,6 +45,7 @@ extern TupleTableSlot *execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot); extern Bitmapset *execute_attr_map_cols(AttrMap *attrMap, Bitmapset *inbitmap); +extern Bitmapset *execute_attr_map_cols_with_null(AttrMap *attrMap, Bitmapset *inbitmap); extern void free_conversion_map(TupleConversionMap *map); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 7795a69..5ba392b 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1213,6 +1213,15 @@ typedef struct ModifyTableState HTAB *mt_resultOidHash; /* optional hash table to speed lookups */ /* + * Cached target partition if all the columns in partition key are + * constant, otherwise NULL + */ + ResultRelInfo *targetpartrel; + Bitmapset *const_bms; /* column numbers of constant value */ + bool cache_routed_rel; /* do we need to cache the target + * partition after tuple routing */ + + /* * Slot for storing tuples in the root partitioned table's rowtype during * an UPDATE of a partitioned table. */ -- 2.7.2.windows.1