From 30c74413e5b0b138048dbd0796ed83fd7027854a Mon Sep 17 00:00:00 2001 From: amit Date: Wed, 6 Nov 2019 11:00:56 +0900 Subject: [PATCH 2/4] Move some code from joinrel.c to relnode.c --- src/backend/optimizer/path/joinrels.c | 177 --------------------------------- src/backend/optimizer/util/relnode.c | 180 ++++++++++++++++++++++++++++++++++ src/include/optimizer/paths.h | 3 - 3 files changed, 180 insertions(+), 180 deletions(-) diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 6c0904b695..fa68059c3f 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -46,8 +46,6 @@ static void try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, static SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo, Relids left_relids, Relids right_relids); -static int match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel, - bool strict_op); /* @@ -1573,178 +1571,3 @@ build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo, return sjinfo; } - -/* - * have_partkey_equi_join - * - * Returns true if there exist equi-join conditions involving pairs - * of matching partition keys of the relations being joined for all - * partition keys. - */ -bool -have_partkey_equi_join(RelOptInfo *joinrel, - RelOptInfo *rel1, RelOptInfo *rel2, - JoinType jointype, List *restrictlist) -{ - PartitionScheme part_scheme = rel1->part_scheme; - ListCell *lc; - int cnt_pks; - bool pk_has_clause[PARTITION_MAX_KEYS]; - bool strict_op; - - /* - * This function should be called when the joining relations have same - * partitioning scheme. - */ - Assert(rel1->part_scheme == rel2->part_scheme); - Assert(part_scheme); - - memset(pk_has_clause, 0, sizeof(pk_has_clause)); - foreach(lc, restrictlist) - { - RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc); - OpExpr *opexpr; - Expr *expr1; - Expr *expr2; - int ipk1; - int ipk2; - - /* If processing an outer join, only use its own join clauses. */ - if (IS_OUTER_JOIN(jointype) && - RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids)) - continue; - - /* Skip clauses which can not be used for a join. */ - if (!rinfo->can_join) - continue; - - /* Skip clauses which are not equality conditions. */ - if (!rinfo->mergeopfamilies && !OidIsValid(rinfo->hashjoinoperator)) - continue; - - opexpr = castNode(OpExpr, rinfo->clause); - - /* - * The equi-join between partition keys is strict if equi-join between - * at least one partition key is using a strict operator. See - * explanation about outer join reordering identity 3 in - * optimizer/README - */ - strict_op = op_strict(opexpr->opno); - - /* Match the operands to the relation. */ - if (bms_is_subset(rinfo->left_relids, rel1->relids) && - bms_is_subset(rinfo->right_relids, rel2->relids)) - { - expr1 = linitial(opexpr->args); - expr2 = lsecond(opexpr->args); - } - else if (bms_is_subset(rinfo->left_relids, rel2->relids) && - bms_is_subset(rinfo->right_relids, rel1->relids)) - { - expr1 = lsecond(opexpr->args); - expr2 = linitial(opexpr->args); - } - else - continue; - - /* - * Only clauses referencing the partition keys are useful for - * partitionwise join. - */ - ipk1 = match_expr_to_partition_keys(expr1, rel1, strict_op); - if (ipk1 < 0) - continue; - ipk2 = match_expr_to_partition_keys(expr2, rel2, strict_op); - if (ipk2 < 0) - continue; - - /* - * If the clause refers to keys at different ordinal positions, it can - * not be used for partitionwise join. - */ - if (ipk1 != ipk2) - continue; - - /* - * The clause allows partitionwise join if only it uses the same - * operator family as that specified by the partition key. - */ - if (rel1->part_scheme->strategy == PARTITION_STRATEGY_HASH) - { - if (!op_in_opfamily(rinfo->hashjoinoperator, - part_scheme->partopfamily[ipk1])) - continue; - } - else if (!list_member_oid(rinfo->mergeopfamilies, - part_scheme->partopfamily[ipk1])) - continue; - - /* Mark the partition key as having an equi-join clause. */ - pk_has_clause[ipk1] = true; - } - - /* Check whether every partition key has an equi-join condition. */ - for (cnt_pks = 0; cnt_pks < part_scheme->partnatts; cnt_pks++) - { - if (!pk_has_clause[cnt_pks]) - return false; - } - - return true; -} - -/* - * match_expr_to_partition_keys - * - * Tries to match an expression to one of the nullable or non-nullable - * partition keys and if a match is found, returns the matched key's - * ordinal position or -1 if the expression could not be matched to any - * of the keys. - * - * strict_op must be true if the expression will be compared with the - * partition key using a strict operator. - */ -static int -match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel, bool strict_op) -{ - int cnt; - - /* This function should be called only for partitioned relations. */ - Assert(rel->part_scheme); - - /* Remove any relabel decorations. */ - while (IsA(expr, RelabelType)) - expr = (Expr *) (castNode(RelabelType, expr))->arg; - - for (cnt = 0; cnt < rel->part_scheme->partnatts; cnt++) - { - ListCell *lc; - - Assert(rel->partexprs); - foreach(lc, rel->partexprs[cnt]) - { - if (equal(lfirst(lc), expr)) - return cnt; - } - - if (!strict_op) - continue; - - /* - * If it's a strict equi-join a NULL partition key on one side will - * not join a NULL partition key on the other side. So, rows with NULL - * partition key from a partition on one side can not join with those - * from a non-matching partition on the other side. So, search the - * nullable partition keys as well. - */ - Assert(rel->nullable_partexprs); - foreach(lc, rel->nullable_partexprs[cnt]) - { - if (equal(lfirst(lc), expr)) - return cnt; - } - } - - return -1; -} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index e30aa692d7..07ece2f870 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -67,6 +67,11 @@ static void build_child_join_reltarget(PlannerInfo *root, RelOptInfo *childrel, int nappinfos, AppendRelInfo **appinfos); +static bool have_partkey_equi_join(RelOptInfo *joinrel, + RelOptInfo *rel1, RelOptInfo *rel2, + JoinType jointype, List *restrictlist); +static int match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel, + bool strict_op); /* @@ -1823,3 +1828,178 @@ build_child_join_reltarget(PlannerInfo *root, childrel->reltarget->cost.per_tuple = parentrel->reltarget->cost.per_tuple; childrel->reltarget->width = parentrel->reltarget->width; } + +/* + * have_partkey_equi_join + * + * Returns true if there exist equi-join conditions involving pairs + * of matching partition keys of the relations being joined for all + * partition keys. + */ +bool +have_partkey_equi_join(RelOptInfo *joinrel, + RelOptInfo *rel1, RelOptInfo *rel2, + JoinType jointype, List *restrictlist) +{ + PartitionScheme part_scheme = rel1->part_scheme; + ListCell *lc; + int cnt_pks; + bool pk_has_clause[PARTITION_MAX_KEYS]; + bool strict_op; + + /* + * This function should be called when the joining relations have same + * partitioning scheme. + */ + Assert(rel1->part_scheme == rel2->part_scheme); + Assert(part_scheme); + + memset(pk_has_clause, 0, sizeof(pk_has_clause)); + foreach(lc, restrictlist) + { + RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc); + OpExpr *opexpr; + Expr *expr1; + Expr *expr2; + int ipk1; + int ipk2; + + /* If processing an outer join, only use its own join clauses. */ + if (IS_OUTER_JOIN(jointype) && + RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids)) + continue; + + /* Skip clauses which can not be used for a join. */ + if (!rinfo->can_join) + continue; + + /* Skip clauses which are not equality conditions. */ + if (!rinfo->mergeopfamilies && !OidIsValid(rinfo->hashjoinoperator)) + continue; + + opexpr = castNode(OpExpr, rinfo->clause); + + /* + * The equi-join between partition keys is strict if equi-join between + * at least one partition key is using a strict operator. See + * explanation about outer join reordering identity 3 in + * optimizer/README + */ + strict_op = op_strict(opexpr->opno); + + /* Match the operands to the relation. */ + if (bms_is_subset(rinfo->left_relids, rel1->relids) && + bms_is_subset(rinfo->right_relids, rel2->relids)) + { + expr1 = linitial(opexpr->args); + expr2 = lsecond(opexpr->args); + } + else if (bms_is_subset(rinfo->left_relids, rel2->relids) && + bms_is_subset(rinfo->right_relids, rel1->relids)) + { + expr1 = lsecond(opexpr->args); + expr2 = linitial(opexpr->args); + } + else + continue; + + /* + * Only clauses referencing the partition keys are useful for + * partitionwise join. + */ + ipk1 = match_expr_to_partition_keys(expr1, rel1, strict_op); + if (ipk1 < 0) + continue; + ipk2 = match_expr_to_partition_keys(expr2, rel2, strict_op); + if (ipk2 < 0) + continue; + + /* + * If the clause refers to keys at different ordinal positions, it can + * not be used for partitionwise join. + */ + if (ipk1 != ipk2) + continue; + + /* + * The clause allows partitionwise join if only it uses the same + * operator family as that specified by the partition key. + */ + if (rel1->part_scheme->strategy == PARTITION_STRATEGY_HASH) + { + if (!op_in_opfamily(rinfo->hashjoinoperator, + part_scheme->partopfamily[ipk1])) + continue; + } + else if (!list_member_oid(rinfo->mergeopfamilies, + part_scheme->partopfamily[ipk1])) + continue; + + /* Mark the partition key as having an equi-join clause. */ + pk_has_clause[ipk1] = true; + } + + /* Check whether every partition key has an equi-join condition. */ + for (cnt_pks = 0; cnt_pks < part_scheme->partnatts; cnt_pks++) + { + if (!pk_has_clause[cnt_pks]) + return false; + } + + return true; +} + +/* + * match_expr_to_partition_keys + * + * Tries to match an expression to one of the nullable or non-nullable + * partition keys and if a match is found, returns the matched key's + * ordinal position or -1 if the expression could not be matched to any + * of the keys. + * + * strict_op must be true if the expression will be compared with the + * partition key using a strict operator. + */ +static int +match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel, bool strict_op) +{ + int cnt; + + /* This function should be called only for partitioned relations. */ + Assert(rel->part_scheme); + + /* Remove any relabel decorations. */ + while (IsA(expr, RelabelType)) + expr = (Expr *) (castNode(RelabelType, expr))->arg; + + for (cnt = 0; cnt < rel->part_scheme->partnatts; cnt++) + { + ListCell *lc; + + Assert(rel->partexprs); + foreach(lc, rel->partexprs[cnt]) + { + if (equal(lfirst(lc), expr)) + return cnt; + } + + if (!strict_op) + continue; + + /* + * If it's a strict equi-join a NULL partition key on one side will + * not join a NULL partition key on the other side. So, rows with NULL + * partition key from a partition on one side can not join with those + * from a non-matching partition on the other side. So, search the + * nullable partition keys as well. + */ + Assert(rel->nullable_partexprs); + foreach(lc, rel->nullable_partexprs[cnt]) + { + if (equal(lfirst(lc), expr)) + return cnt; + } + } + + return -1; +} diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index c6c34630c2..1d74faddb8 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -106,9 +106,6 @@ extern bool have_join_order_restriction(PlannerInfo *root, extern bool have_dangerous_phv(PlannerInfo *root, Relids outer_relids, Relids inner_params); extern void mark_dummy_rel(RelOptInfo *rel); -extern bool have_partkey_equi_join(RelOptInfo *joinrel, - RelOptInfo *rel1, RelOptInfo *rel2, - JoinType jointype, List *restrictlist); /* * equivclass.c -- 2.11.0