recursively plan right side if constant false join clause exists between 2 nonrecurring parts

outer-join-noncolocated-dist-tables
aykutbozkurt 2023-01-30 10:37:51 +03:00
parent 4f84259c2a
commit 29f4b18156
5 changed files with 135 additions and 4 deletions

View File

@ -1208,6 +1208,19 @@ static JoinOrderNode *
CartesianProductReferenceJoin(JoinOrderNode *currentJoinNode, TableEntry *candidateTable, CartesianProductReferenceJoin(JoinOrderNode *currentJoinNode, TableEntry *candidateTable,
List *applicableJoinClauses, JoinType joinType) 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( bool leftIsReferenceTable = IsCitusTableType(
currentJoinNode->tableEntry->relationId, currentJoinNode->tableEntry->relationId,
REFERENCE_TABLE); REFERENCE_TABLE);

View File

@ -152,6 +152,8 @@ static void RecursivelyPlanNonColocatedSubqueriesInWhere(Query *query,
recursivePlanningContext); recursivePlanningContext);
static bool RecursivelyPlanRecurringTupleOuterJoinWalker(Node *node, Query *query, static bool RecursivelyPlanRecurringTupleOuterJoinWalker(Node *node, Query *query,
RecursivePlanningContext *context); RecursivePlanningContext *context);
static bool RecursivelyPlanConstantFalseJoinsWalker(Node *node, Query *query,
RecursivePlanningContext *context);
static void RecursivelyPlanDistributedJoinNode(Node *node, Query *query, static void RecursivelyPlanDistributedJoinNode(Node *node, Query *query,
RecursivePlanningContext *context); RecursivePlanningContext *context);
static bool IsRTERefRecurring(RangeTblRef *rangeTableRef, Query *query); static bool IsRTERefRecurring(RangeTblRef *rangeTableRef, Query *query);
@ -383,10 +385,107 @@ RecursivelyPlanSubqueriesAndCTEs(Query *query, RecursivePlanningContext *context
RecursivelyPlanAllSubqueries((Node *) query->targetList, 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; 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 * GetPlannerRestrictionContext returns the planner restriction context
* from the given context. * from the given context.

View File

@ -520,12 +520,27 @@ SELECT
FROM FROM
orders INNER JOIN customer_append ON (o_custkey = c_custkey AND false); orders INNER JOIN customer_append ON (o_custkey = c_custkey AND false);
DEBUG: Router planner does not support append-partitioned tables. 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) 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 Tasks Shown: All
(3 rows) -> Task
Node: host=localhost port=xxxxx dbname=regression
-> Result
One-Time Filter: false
(11 rows)
EXPLAIN (COSTS OFF) EXPLAIN (COSTS OFF)
SELECT SELECT

View File

@ -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. --- 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. -- 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; 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 | | 1 | 2 | |
3 | 4 | | 3 | 4 | |
(2 rows) (2 rows)

View File

@ -1123,6 +1123,8 @@ FROM
correlated_subquery_view correlated_subquery_view
LEFT JOIN LATERAL 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); (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 sum
--------------------------------------------------------------------- ---------------------------------------------------------------------