mirror of https://github.com/citusdata/citus.git
recursively plan right side if constant false join clause exists between 2 nonrecurring parts
parent
4f84259c2a
commit
29f4b18156
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
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)
|
||||
-> Distributed Subplan XXX_1
|
||||
-> Custom Scan (Citus Adaptive)
|
||||
Task Count: 0
|
||||
Tasks Shown: All
|
||||
(3 rows)
|
||||
Task Count: 1
|
||||
Tasks Shown: All
|
||||
-> Task
|
||||
Node: host=localhost port=xxxxx dbname=regression
|
||||
-> Result
|
||||
One-Time Filter: false
|
||||
(11 rows)
|
||||
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
|
Loading…
Reference in New Issue