Search all outer node levels for lateral join params.

pull/2654/head
Hadi Moshayedi 2019-04-04 14:05:41 -07:00 committed by Hadi Moshayedi
parent 5cc8049caa
commit 8e2d328530
6 changed files with 153 additions and 28 deletions

View File

@ -79,6 +79,7 @@ static void CheckNodeIsDumpable(Node *node);
static Node * CheckNodeCopyAndSerialization(Node *node); static Node * CheckNodeCopyAndSerialization(Node *node);
static void AdjustReadIntermediateResultCost(RangeTblEntry *rangeTableEntry, static void AdjustReadIntermediateResultCost(RangeTblEntry *rangeTableEntry,
RelOptInfo *relOptInfo); RelOptInfo *relOptInfo);
static List * OuterPlanParamsList(PlannerInfo *root);
static List * CopyPlanParamList(List *originalPlanParamList); static List * CopyPlanParamList(List *originalPlanParamList);
static PlannerRestrictionContext * CreateAndPushPlannerRestrictionContext(void); static PlannerRestrictionContext * CreateAndPushPlannerRestrictionContext(void);
static PlannerRestrictionContext * CurrentPlannerRestrictionContext(void); static PlannerRestrictionContext * CurrentPlannerRestrictionContext(void);
@ -1299,15 +1300,10 @@ multi_relation_restriction_hook(PlannerInfo *root, RelOptInfo *relOptInfo, Index
relationRestriction->relOptInfo = relOptInfo; relationRestriction->relOptInfo = relOptInfo;
relationRestriction->distributedRelation = distributedTable; relationRestriction->distributedRelation = distributedTable;
relationRestriction->plannerInfo = root; relationRestriction->plannerInfo = root;
relationRestriction->parentPlannerInfo = root->parent_root;
relationRestriction->prunedShardIntervalList = NIL; relationRestriction->prunedShardIntervalList = NIL;
/* see comments on GetVarFromAssignedParam() */ /* see comments on GetVarFromAssignedParam() */
if (relationRestriction->parentPlannerInfo) relationRestriction->outerPlanParamsList = OuterPlanParamsList(root);
{
relationRestriction->parentPlannerParamList =
CopyPlanParamList(root->parent_root->plan_params);
}
relationRestrictionContext = plannerRestrictionContext->relationRestrictionContext; relationRestrictionContext = plannerRestrictionContext->relationRestrictionContext;
relationRestrictionContext->hasDistributedRelation |= distributedTable; relationRestrictionContext->hasDistributedRelation |= distributedTable;
@ -1462,6 +1458,36 @@ AdjustReadIntermediateResultCost(RangeTblEntry *rangeTableEntry, RelOptInfo *rel
} }
/*
* OuterPlanParamsList creates a list of RootPlanParams for outer nodes of the
* given root. The first item in the list corresponds to parent_root, and the
* last item corresponds to the outer most node.
*/
static List *
OuterPlanParamsList(PlannerInfo *root)
{
List *planParamsList = NIL;
PlannerInfo *outerNodeRoot = NULL;
for (outerNodeRoot = root->parent_root; outerNodeRoot != NULL;
outerNodeRoot = outerNodeRoot->parent_root)
{
RootPlanParams *rootPlanParams = palloc0(sizeof(RootPlanParams));
rootPlanParams->root = outerNodeRoot;
/*
* TODO: In SearchPlannerParamList() we are only interested in Var plan
* params, consider copying just them here.
*/
rootPlanParams->plan_params = CopyPlanParamList(outerNodeRoot->plan_params);
planParamsList = lappend(planParamsList, rootPlanParams);
}
return planParamsList;
}
/* /*
* CopyPlanParamList deep copies the input PlannerParamItem list and returns the newly * CopyPlanParamList deep copies the input PlannerParamItem list and returns the newly
* allocated list. * allocated list.

View File

@ -98,8 +98,9 @@ static void AddRteRelationToAttributeEquivalenceClass(AttributeEquivalenceClass
attrEquivalenceClass, attrEquivalenceClass,
RangeTblEntry *rangeTableEntry, RangeTblEntry *rangeTableEntry,
Var *varToBeAdded); Var *varToBeAdded);
static Var * GetVarFromAssignedParam(List *parentPlannerParamList, static Var * GetVarFromAssignedParam(List *outerPlanParamsList, Param *plannerParam,
Param *plannerParam); PlannerInfo **rootContainingVar);
static Var * SearchPlannerParamList(List *plannerParamList, Param *plannerParam);
static List * GenerateAttributeEquivalencesForJoinRestrictions(JoinRestrictionContext static List * GenerateAttributeEquivalencesForJoinRestrictions(JoinRestrictionContext
*joinRestrictionContext); *joinRestrictionContext);
static bool AttributeClassContainsAttributeClassMember(AttributeEquivalenceClassMember * static bool AttributeClassContainsAttributeClassMember(AttributeEquivalenceClassMember *
@ -737,15 +738,15 @@ AttributeEquivalenceClassForEquivalenceClass(EquivalenceClass *plannerEqClass,
if (IsA(strippedEquivalenceExpr, Param)) if (IsA(strippedEquivalenceExpr, Param))
{ {
List *parentParamList = relationRestriction->parentPlannerParamList; PlannerInfo *outerNodeRoot = NULL;
Param *equivalenceParam = (Param *) strippedEquivalenceExpr; Param *equivalenceParam = (Param *) strippedEquivalenceExpr;
expressionVar = GetVarFromAssignedParam(parentParamList, expressionVar =
equivalenceParam); GetVarFromAssignedParam(relationRestriction->outerPlanParamsList,
equivalenceParam, &outerNodeRoot);
if (expressionVar) if (expressionVar)
{ {
AddToAttributeEquivalenceClass(&attributeEquivalance, AddToAttributeEquivalenceClass(&attributeEquivalance, outerNodeRoot,
relationRestriction->parentPlannerInfo,
expressionVar); expressionVar);
} }
} }
@ -766,7 +767,7 @@ AttributeEquivalenceClassForEquivalenceClass(EquivalenceClass *plannerEqClass,
* plannerParam if its kind is PARAM_EXEC. * plannerParam if its kind is PARAM_EXEC.
* *
* If the paramkind is not equal to PARAM_EXEC the function returns NULL. Similarly, * If the paramkind is not equal to PARAM_EXEC the function returns NULL. Similarly,
* if there is no var that the given param is assigned to, the function returns NULL. * if there is no Var corresponding to the given param is, the function returns NULL.
* *
* Rationale behind this function: * Rationale behind this function:
* *
@ -775,26 +776,26 @@ AttributeEquivalenceClassForEquivalenceClass(EquivalenceClass *plannerEqClass,
* the RTE_RELATIONs which actually belong to lateral vars from the other query * the RTE_RELATIONs which actually belong to lateral vars from the other query
* levels. * levels.
* *
* We're also keeping track of the RTE_RELATION's parent_root's * We're also keeping track of the RTE_RELATION's outer nodes'
* plan_param list which is expected to hold the parameters that are required * plan_params lists which is expected to hold the parameters that are required
* for its lower level queries as it is documented: * for its lower level queries as it is documented:
* *
* plan_params contains the expressions that this query level needs to * plan_params contains the expressions that this query level needs to
* make available to a lower query level that is currently being planned. * make available to a lower query level that is currently being planned.
* *
* This function is a helper function to iterate through the parent query's * This function is a helper function to iterate through the outer node's query's
* plan_params and looks for the param that the equivalence member has. The * plan_params and looks for the param that the equivalence member has. The
* comparison is done via the "paramid" field. Finally, if the found parameter's * comparison is done via the "paramid" field. Finally, if the found parameter's
* item is a Var, we conclude that Postgres standard_planner replaced the Var * item is a Var, we conclude that Postgres standard_planner replaced the Var
* with the Param on assign_param_for_var() function * with the Param on assign_param_for_var() function
* @src/backend/optimizer//plan/subselect.c. * @src/backend/optimizer/plan/subselect.c.
*
*/ */
static Var * static Var *
GetVarFromAssignedParam(List *parentPlannerParamList, Param *plannerParam) GetVarFromAssignedParam(List *outerPlanParamsList, Param *plannerParam,
PlannerInfo **rootContainingVar)
{ {
Var *assignedVar = NULL; Var *assignedVar = NULL;
ListCell *plannerParameterCell = NULL; ListCell *rootPlanParamsCell = NULL;
Assert(plannerParam != NULL); Assert(plannerParam != NULL);
@ -804,7 +805,35 @@ GetVarFromAssignedParam(List *parentPlannerParamList, Param *plannerParam)
return NULL; return NULL;
} }
foreach(plannerParameterCell, parentPlannerParamList) foreach(rootPlanParamsCell, outerPlanParamsList)
{
RootPlanParams *outerPlanParams = lfirst(rootPlanParamsCell);
assignedVar = SearchPlannerParamList(outerPlanParams->plan_params,
plannerParam);
if (assignedVar != NULL)
{
*rootContainingVar = outerPlanParams->root;
break;
}
}
return assignedVar;
}
/*
* SearchPlannerParamList searches in plannerParamList and returns the Var that
* corresponds to the given plannerParam. If there is no Var corresponding to the
* given param is, the function returns NULL.
*/
static Var *
SearchPlannerParamList(List *plannerParamList, Param *plannerParam)
{
Var *assignedVar = NULL;
ListCell *plannerParameterCell = NULL;
foreach(plannerParameterCell, plannerParamList)
{ {
PlannerParamItem *plannerParamItem = PlannerParamItem *plannerParamItem =
(PlannerParamItem *) lfirst(plannerParameterCell); (PlannerParamItem *) lfirst(plannerParameterCell);
@ -814,7 +843,7 @@ GetVarFromAssignedParam(List *parentPlannerParamList, Param *plannerParam)
continue; continue;
} }
/* TODO: Should we consider PlaceHolderVar?? */ /* TODO: Should we consider PlaceHolderVar? */
if (!IsA(plannerParamItem->item, Var)) if (!IsA(plannerParamItem->item, Var))
{ {
continue; continue;

View File

@ -32,6 +32,18 @@ typedef struct RelationRestrictionContext
List *relationRestrictionList; List *relationRestrictionList;
} RelationRestrictionContext; } RelationRestrictionContext;
typedef struct RootPlanParams
{
PlannerInfo *root;
/*
* Copy of root->plan_params. root->plan_params is not preserved in
* relation_restriction_equivalence, so we need to create a copy.
*/
List *plan_params;
} RootPlanParams;
typedef struct RelationRestriction typedef struct RelationRestriction
{ {
Index index; Index index;
@ -40,9 +52,10 @@ typedef struct RelationRestriction
RangeTblEntry *rte; RangeTblEntry *rte;
RelOptInfo *relOptInfo; RelOptInfo *relOptInfo;
PlannerInfo *plannerInfo; PlannerInfo *plannerInfo;
PlannerInfo *parentPlannerInfo;
List *parentPlannerParamList;
List *prunedShardIntervalList; List *prunedShardIntervalList;
/* list of RootPlanParams for all outer nodes */
List *outerPlanParamsList;
} RelationRestriction; } RelationRestriction;
typedef struct JoinRestrictionContext typedef struct JoinRestrictionContext

View File

@ -2249,6 +2249,37 @@ FROM
6 | 6 |
(1 row) (1 row)
-- Test the case when a subquery has a lateral reference to two levels upper
SELECT
b.user_id, b.value_2, b.cnt
FROM (
SELECT
user_id,
value_2
FROM events_table
WHERE events_table.user_id BETWEEN 2 AND 5
) a,
LATERAL (
SELECT
user_id, value_2, count(*) as cnt
FROM (
SELECT
value_2, time, user_id
FROM events_table
WHERE user_id BETWEEN 2 AND 5
AND events_table.user_id = a.user_id
AND events_table.value_2 = a.value_2
ORDER BY time DESC
) events
GROUP BY user_id, value_2
) b
ORDER BY user_id, value_2, cnt
LIMIT 1;
user_id | value_2 | cnt
---------+---------+-----
2 | 0 | 1
(1 row)
DROP FUNCTION test_join_function_2(integer, integer); DROP FUNCTION test_join_function_2(integer, integer);
SELECT run_command_on_workers($f$ SELECT run_command_on_workers($f$

View File

@ -671,12 +671,11 @@ SELECT * FROM test a WHERE x IN (SELECT x FROM test b UNION SELECT y FROM test c
DEBUG: generating subplan 144_1 for subquery SELECT x FROM recursive_union.test b DEBUG: generating subplan 144_1 for subquery SELECT x FROM recursive_union.test b
DEBUG: skipping recursive planning for the subquery since it contains references to outer queries DEBUG: skipping recursive planning for the subquery since it contains references to outer queries
DEBUG: skipping recursive planning for the subquery since it contains references to outer queries DEBUG: skipping recursive planning for the subquery since it contains references to outer queries
DEBUG: skipping recursive planning for the subquery since it contains references to outer queries
DEBUG: Plan 144 query after replacing subqueries and CTEs: SELECT x, y FROM recursive_union.test a WHERE (x OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.x FROM read_intermediate_result('144_1'::text, 'binary'::citus_copy_format) intermediate_result(x integer) UNION SELECT c.y FROM recursive_union.test c WHERE (a.x OPERATOR(pg_catalog.=) c.x))) ORDER BY x, y DEBUG: Plan 144 query after replacing subqueries and CTEs: SELECT x, y FROM recursive_union.test a WHERE (x OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.x FROM read_intermediate_result('144_1'::text, 'binary'::citus_copy_format) intermediate_result(x integer) UNION SELECT c.y FROM recursive_union.test c WHERE (a.x OPERATOR(pg_catalog.=) c.x))) ORDER BY x, y
DEBUG: skipping recursive planning for the subquery since it contains references to outer queries DEBUG: skipping recursive planning for the subquery since it contains references to outer queries
DEBUG: skipping recursive planning for the subquery since it contains references to outer queries DEBUG: skipping recursive planning for the subquery since it contains references to outer queries
DEBUG: skipping recursive planning for the subquery since it contains references to outer queries ERROR: cannot push down this subquery
ERROR: complex joins are only supported when all distributed tables are joined on their distribution columns with equal operator DETAIL: Complex subqueries and CTEs are not supported within a UNION
-- force unions to be planned while subqueries are being planned -- force unions to be planned while subqueries are being planned
SELECT * FROM ((SELECT * FROM test) UNION (SELECT * FROM test) ORDER BY 1,2 LIMIT 5) as foo ORDER BY 1 DESC LIMIT 3; SELECT * FROM ((SELECT * FROM test) UNION (SELECT * FROM test) ORDER BY 1,2 LIMIT 5) as foo ORDER BY 1 DESC LIMIT 3;
DEBUG: generating subplan 147_1 for subquery SELECT x, y FROM recursive_union.test DEBUG: generating subplan 147_1 for subquery SELECT x, y FROM recursive_union.test

View File

@ -1839,6 +1839,33 @@ FROM
) AS temp; ) AS temp;
-- Test the case when a subquery has a lateral reference to two levels upper
SELECT
b.user_id, b.value_2, b.cnt
FROM (
SELECT
user_id,
value_2
FROM events_table
WHERE events_table.user_id BETWEEN 2 AND 5
) a,
LATERAL (
SELECT
user_id, value_2, count(*) as cnt
FROM (
SELECT
value_2, time, user_id
FROM events_table
WHERE user_id BETWEEN 2 AND 5
AND events_table.user_id = a.user_id
AND events_table.value_2 = a.value_2
ORDER BY time DESC
) events
GROUP BY user_id, value_2
) b
ORDER BY user_id, value_2, cnt
LIMIT 1;
DROP FUNCTION test_join_function_2(integer, integer); DROP FUNCTION test_join_function_2(integer, integer);