Fix "wrong varnullingrels" for subquery nestloop parameters.
If we apply outer join identity 3 when relation C is a subquery having lateral references to relation B, then the lateral references within C continue to bear the original syntactically-correct varnullingrels marks, but that won't match what is available from the outer side of the nestloop. Compensate for that in process_subquery_nestloop_params(). This is a slightly hacky fix, but we certainly don't want to re-plan C in toto for each possible outer join order, so there's not a lot of better alternatives. Richard Guo and Tom Lane, per report from Markus Winand Discussion: https://postgr.es/m/DFBB2D25-DE97-49CA-A60E-07C881EA59A7@winand.at
This commit is contained in:
parent
548d726030
commit
bfd332b3fd
|
@ -2289,9 +2289,11 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
|
||||||
* the outer-join level at which they are used, Vars seen in the
|
* the outer-join level at which they are used, Vars seen in the
|
||||||
* NestLoopParam expression may have nullingrels that are just a
|
* NestLoopParam expression may have nullingrels that are just a
|
||||||
* subset of those in the Vars actually available from the outer
|
* subset of those in the Vars actually available from the outer
|
||||||
* side. Not checking this exactly is a bit grotty, but the work
|
* side. Another case that can cause that to happen is explained
|
||||||
* needed to make things match up perfectly seems well out of
|
* in the comments for process_subquery_nestloop_params. Not
|
||||||
* proportion to the value.
|
* checking this exactly is a bit grotty, but the work needed to
|
||||||
|
* make things match up perfectly seems well out of proportion to
|
||||||
|
* the value.
|
||||||
*/
|
*/
|
||||||
nlp->paramval = (Var *) fix_upper_expr(root,
|
nlp->paramval = (Var *) fix_upper_expr(root,
|
||||||
(Node *) nlp->paramval,
|
(Node *) nlp->paramval,
|
||||||
|
|
|
@ -421,8 +421,26 @@ replace_nestloop_param_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
|
||||||
* provide these values. This differs from replace_nestloop_param_var in
|
* provide these values. This differs from replace_nestloop_param_var in
|
||||||
* that the PARAM_EXEC slots to use have already been determined.
|
* that the PARAM_EXEC slots to use have already been determined.
|
||||||
*
|
*
|
||||||
|
* An additional complication is that the subplan_params may contain
|
||||||
|
* nullingrel markers that need adjustment. This occurs if we have applied
|
||||||
|
* outer join identity 3,
|
||||||
|
* (A leftjoin B on (Pab)) leftjoin C on (Pb*c)
|
||||||
|
* = A leftjoin (B leftjoin C on (Pbc)) on (Pab)
|
||||||
|
* and C is a subquery containing lateral references to B. It's still safe
|
||||||
|
* to apply the identity, but the parser will have created those references
|
||||||
|
* in the form "b*" (i.e., with varnullingrels listing the A/B join), while
|
||||||
|
* what we will have available from the nestloop's outer side is just "b".
|
||||||
|
* We deal with that here by stripping the nullingrels down to what is
|
||||||
|
* available from the outer side according to root->curOuterRels.
|
||||||
|
* That fixes matters for the case of forward application of identity 3.
|
||||||
|
* If the identity was applied in the reverse direction, we will have
|
||||||
|
* subplan_params containing too few nullingrel bits rather than too many.
|
||||||
|
* Currently, that causes no problems because setrefs.c applies only a
|
||||||
|
* subset check to nullingrels in NestLoopParams, but we'd have to work
|
||||||
|
* harder if we ever want to tighten that check.
|
||||||
|
*
|
||||||
* Note that we also use root->curOuterRels as an implicit parameter for
|
* Note that we also use root->curOuterRels as an implicit parameter for
|
||||||
* sanity checks.
|
* sanity checks and nullingrel adjustments.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
|
process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
|
||||||
|
@ -449,17 +467,19 @@ process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
|
||||||
nlp = (NestLoopParam *) lfirst(lc2);
|
nlp = (NestLoopParam *) lfirst(lc2);
|
||||||
if (nlp->paramno == pitem->paramId)
|
if (nlp->paramno == pitem->paramId)
|
||||||
{
|
{
|
||||||
Assert(equal(var, nlp->paramval));
|
|
||||||
/* Present, so nothing to do */
|
/* Present, so nothing to do */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (lc2 == NULL)
|
if (lc2 == NULL)
|
||||||
{
|
{
|
||||||
/* No, so add it */
|
/* No, so add it after adjusting its nullingrels */
|
||||||
|
var = copyObject(var);
|
||||||
|
var->varnullingrels = bms_intersect(var->varnullingrels,
|
||||||
|
root->curOuterRels);
|
||||||
nlp = makeNode(NestLoopParam);
|
nlp = makeNode(NestLoopParam);
|
||||||
nlp->paramno = pitem->paramId;
|
nlp->paramno = pitem->paramId;
|
||||||
nlp->paramval = copyObject(var);
|
nlp->paramval = var;
|
||||||
root->curOuterParams = lappend(root->curOuterParams, nlp);
|
root->curOuterParams = lappend(root->curOuterParams, nlp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -480,17 +500,19 @@ process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
|
||||||
nlp = (NestLoopParam *) lfirst(lc2);
|
nlp = (NestLoopParam *) lfirst(lc2);
|
||||||
if (nlp->paramno == pitem->paramId)
|
if (nlp->paramno == pitem->paramId)
|
||||||
{
|
{
|
||||||
Assert(equal(phv, nlp->paramval));
|
|
||||||
/* Present, so nothing to do */
|
/* Present, so nothing to do */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (lc2 == NULL)
|
if (lc2 == NULL)
|
||||||
{
|
{
|
||||||
/* No, so add it */
|
/* No, so add it after adjusting its nullingrels */
|
||||||
|
phv = copyObject(phv);
|
||||||
|
phv->phnullingrels = bms_intersect(phv->phnullingrels,
|
||||||
|
root->curOuterRels);
|
||||||
nlp = makeNode(NestLoopParam);
|
nlp = makeNode(NestLoopParam);
|
||||||
nlp->paramno = pitem->paramId;
|
nlp->paramno = pitem->paramId;
|
||||||
nlp->paramval = (Var *) copyObject(phv);
|
nlp->paramval = (Var *) phv;
|
||||||
root->curOuterParams = lappend(root->curOuterParams, nlp);
|
root->curOuterParams = lappend(root->curOuterParams, nlp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2589,6 +2589,24 @@ on t2.q2 = 123;
|
||||||
-> Seq Scan on int8_tbl t5
|
-> Seq Scan on int8_tbl t5
|
||||||
(12 rows)
|
(12 rows)
|
||||||
|
|
||||||
|
explain (costs off)
|
||||||
|
select * from int8_tbl t1
|
||||||
|
left join int8_tbl t2 on true
|
||||||
|
left join lateral
|
||||||
|
(select * from int8_tbl t3 where t3.q1 = t2.q1 offset 0) s
|
||||||
|
on t2.q1 = 1;
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------
|
||||||
|
Nested Loop Left Join
|
||||||
|
-> Seq Scan on int8_tbl t1
|
||||||
|
-> Materialize
|
||||||
|
-> Nested Loop Left Join
|
||||||
|
Join Filter: (t2.q1 = 1)
|
||||||
|
-> Seq Scan on int8_tbl t2
|
||||||
|
-> Seq Scan on int8_tbl t3
|
||||||
|
Filter: (q1 = t2.q1)
|
||||||
|
(8 rows)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- check a case where we formerly got confused by conflicting sort orders
|
-- check a case where we formerly got confused by conflicting sort orders
|
||||||
-- in redundant merge join path keys
|
-- in redundant merge join path keys
|
||||||
|
|
|
@ -514,6 +514,13 @@ select * from int8_tbl t1 left join
|
||||||
left join int8_tbl t5 on t2.q1 = t5.q1
|
left join int8_tbl t5 on t2.q1 = t5.q1
|
||||||
on t2.q2 = 123;
|
on t2.q2 = 123;
|
||||||
|
|
||||||
|
explain (costs off)
|
||||||
|
select * from int8_tbl t1
|
||||||
|
left join int8_tbl t2 on true
|
||||||
|
left join lateral
|
||||||
|
(select * from int8_tbl t3 where t3.q1 = t2.q1 offset 0) s
|
||||||
|
on t2.q1 = 1;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- check a case where we formerly got confused by conflicting sort orders
|
-- check a case where we formerly got confused by conflicting sort orders
|
||||||
-- in redundant merge join path keys
|
-- in redundant merge join path keys
|
||||||
|
|
Loading…
Reference in New Issue