From 29f4b181568c3111d7146cd51f3feb3274e55ceb Mon Sep 17 00:00:00 2001 From: aykutbozkurt Date: Mon, 30 Jan 2023 10:37:51 +0300 Subject: [PATCH] recursively plan right side if constant false join clause exists between 2 nonrecurring parts --- .../distributed/planner/multi_join_order.c | 13 +++ .../distributed/planner/recursive_planning.c | 99 +++++++++++++++++++ .../multi_repartition_join_pruning.out | 21 +++- .../expected/non_colocated_outer_joins.out | 4 +- .../regress/expected/subquery_in_where.out | 2 + 5 files changed, 135 insertions(+), 4 deletions(-) diff --git a/src/backend/distributed/planner/multi_join_order.c b/src/backend/distributed/planner/multi_join_order.c index 29bfe6a27..e5d598c81 100644 --- a/src/backend/distributed/planner/multi_join_order.c +++ b/src/backend/distributed/planner/multi_join_order.c @@ -1208,6 +1208,19 @@ static JoinOrderNode * CartesianProductReferenceJoin(JoinOrderNode *currentJoinNode, TableEntry *candidateTable, List *applicableJoinClauses, JoinType joinType) { + /* + * right distributed part is already recursively planned. + * constant false join is supported as CartesianProductReferenceJoin. + */ + if (list_length(applicableJoinClauses) == 1 && + ContainsFalseClause(applicableJoinClauses)) + { + return MakeJoinOrderNode(candidateTable, CARTESIAN_PRODUCT_REFERENCE_JOIN, + currentJoinNode->partitionColumnList, + currentJoinNode->partitionMethod, + currentJoinNode->anchorTable, joinType); + } + bool leftIsReferenceTable = IsCitusTableType( currentJoinNode->tableEntry->relationId, REFERENCE_TABLE); diff --git a/src/backend/distributed/planner/recursive_planning.c b/src/backend/distributed/planner/recursive_planning.c index 936b17364..af186a2a1 100644 --- a/src/backend/distributed/planner/recursive_planning.c +++ b/src/backend/distributed/planner/recursive_planning.c @@ -152,6 +152,8 @@ static void RecursivelyPlanNonColocatedSubqueriesInWhere(Query *query, recursivePlanningContext); static bool RecursivelyPlanRecurringTupleOuterJoinWalker(Node *node, Query *query, RecursivePlanningContext *context); +static bool RecursivelyPlanConstantFalseJoinsWalker(Node *node, Query *query, + RecursivePlanningContext *context); static void RecursivelyPlanDistributedJoinNode(Node *node, Query *query, RecursivePlanningContext *context); static bool IsRTERefRecurring(RangeTblRef *rangeTableRef, Query *query); @@ -383,10 +385,107 @@ RecursivelyPlanSubqueriesAndCTEs(Query *query, RecursivePlanningContext *context RecursivelyPlanAllSubqueries((Node *) query->targetList, context); } + /* + * make right side recurring when query has constant false join + * with both side distributed + */ + RecursivelyPlanConstantFalseJoinsWalker((Node *) query->jointree, + query, context); + return NULL; } +/* + * RecursivelyPlanConstantFalseJoinsWalker replaces right side of the joins with constant + * false clause. + * + * Example: + * SELECT dist [FULL/LEFT/RIGHT/INNER] JOIN dist ON (false) + * is converted to + * SELECT dist [FULL/LEFT/RIGHT/INNER] JOIN intermediate_result ON (false) + */ +static bool +RecursivelyPlanConstantFalseJoinsWalker(Node *node, Query *query, + RecursivePlanningContext *context) +{ + if (node == NULL) + { + return false; + } + else if (IsA(node, FromExpr)) + { + FromExpr *fromExpr = (FromExpr *) node; + ListCell *fromExprCell; + + /* search for join trees in each FROM element */ + foreach(fromExprCell, fromExpr->fromlist) + { + Node *fromElement = (Node *) lfirst(fromExprCell); + + RecursivelyPlanConstantFalseJoinsWalker(fromElement, query, + context); + } + + return false; + } + else if (IsA(node, JoinExpr)) + { + JoinExpr *joinExpr = (JoinExpr *) node; + + Node *leftNode = joinExpr->larg; + Node *rightNode = joinExpr->rarg; + Node *joinQualsNode = joinExpr->quals; + List *joinQualList = NIL; + + if (joinQualsNode == NULL) + { + joinQualList = NIL; + } + else if (IsA(joinQualsNode, List)) + { + joinQualList = (List *) joinQualsNode; + } + else + { + /* this part of code only run for subqueries */ + Node *joinClause = eval_const_expressions(NULL, joinQualsNode); + joinClause = (Node *) canonicalize_qual((Expr *) joinClause, false); + joinQualList = make_ands_implicit((Expr *) joinClause); + } + + bool hasConstantFalseJoin = ContainsFalseClause(joinQualList); + + bool isLeftRefRecurring = RecursivelyPlanConstantFalseJoinsWalker(leftNode, query, + context); + bool isRightRefRecurring = RecursivelyPlanConstantFalseJoinsWalker(rightNode, + query, + context); + + if (!isLeftRefRecurring && !isRightRefRecurring && hasConstantFalseJoin) + { + ereport(DEBUG1, (errmsg("recursively planning right side of " + "the constant false join"))); + RecursivelyPlanDistributedJoinNode(rightNode, query, + context); + } + + return isLeftRefRecurring || isRightRefRecurring; + } + else if (IsA(node, RangeTblRef)) + { + RangeTblRef *rangeTableRef = castNode(RangeTblRef, node); + return IsRTERefRecurring(rangeTableRef, query); + } + else + { + ereport(ERROR, errmsg("got unexpected node type (%d) when recursively " + "planning a constant false join", + nodeTag(node))); + } +} + + /* * GetPlannerRestrictionContext returns the planner restriction context * from the given context. diff --git a/src/test/regress/expected/multi_repartition_join_pruning.out b/src/test/regress/expected/multi_repartition_join_pruning.out index f2d67d58b..09294c0cb 100644 --- a/src/test/regress/expected/multi_repartition_join_pruning.out +++ b/src/test/regress/expected/multi_repartition_join_pruning.out @@ -520,12 +520,27 @@ SELECT FROM orders INNER JOIN customer_append ON (o_custkey = c_custkey AND false); DEBUG: Router planner does not support append-partitioned tables. - QUERY PLAN +DEBUG: recursively planning right side of the constant false join +DEBUG: recursively planning distributed relation "customer_append" since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: Wrapping relation "customer_append" to a subquery +DEBUG: Router planner does not support append-partitioned tables. +DEBUG: generating subplan XXX_1 for subquery SELECT NULL::integer AS "dummy-1" FROM public.customer_append WHERE false +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT orders.o_orderkey FROM (public.orders JOIN (SELECT NULL::integer AS c_custkey, NULL::character varying(25) AS c_name, NULL::character varying(40) AS c_address, NULL::integer AS c_nationkey, NULL::character(15) AS c_phone, NULL::numeric(15,2) AS c_acctbal, NULL::character(10) AS c_mktsegment, NULL::character varying(117) AS c_comment FROM (SELECT intermediate_result."dummy-1" FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result("dummy-1" integer)) customer_append_1) customer_append ON (((orders.o_custkey OPERATOR(pg_catalog.=) customer_append.c_custkey) AND false))) +DEBUG: Creating router plan + QUERY PLAN --------------------------------------------------------------------- Custom Scan (Citus Adaptive) - Task Count: 0 + -> Distributed Subplan XXX_1 + -> Custom Scan (Citus Adaptive) + Task Count: 0 + Tasks Shown: All + Task Count: 1 Tasks Shown: All -(3 rows) + -> Task + Node: host=localhost port=xxxxx dbname=regression + -> Result + One-Time Filter: false +(11 rows) EXPLAIN (COSTS OFF) SELECT diff --git a/src/test/regress/expected/non_colocated_outer_joins.out b/src/test/regress/expected/non_colocated_outer_joins.out index addadc87b..b69b0c1f3 100644 --- a/src/test/regress/expected/non_colocated_outer_joins.out +++ b/src/test/regress/expected/non_colocated_outer_joins.out @@ -1134,7 +1134,9 @@ SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.x = dist2.x) WHERE dist1.x >2 ORDE --- constant false filter as join filter for left join. -- inner table will be converted to empty result. Constant filter will be applied before join but will not be pushdowned. SELECT * FROM dist1 LEFT JOIN dist2 ON (dist1.y = dist2.y AND false) ORDER BY 1,2,3,4; -LOG: join order: [ "dist1" ][ cartesian product(LEFT) "dist2" ] +LOG: join order: [ "dist2" ] + x | y | x | y +--------------------------------------------------------------------- 1 | 2 | | 3 | 4 | | (2 rows) diff --git a/src/test/regress/expected/subquery_in_where.out b/src/test/regress/expected/subquery_in_where.out index eb56acd87..1d2b24c58 100644 --- a/src/test/regress/expected/subquery_in_where.out +++ b/src/test/regress/expected/subquery_in_where.out @@ -1123,6 +1123,8 @@ FROM correlated_subquery_view LEFT JOIN LATERAL (SELECT max(value_2) as mx FROM events_table WHERE correlated_subquery_view.user_id = events_table.user_id) as foo ON (false); +DEBUG: recursively planning right side of the constant false join +DEBUG: recursively planning the distributed subquery since it is part of a distributed join node that is outer joined with a recurring rel sum ---------------------------------------------------------------------