Fix "wrong varnullingrels" for Memoize's lateral references, too.
The issue fixed in commit bfd332b3f
can also bite Memoize plans,
because of the separate copies of lateral reference Vars made
by paraminfo_get_equal_hashops. Apply the same hacky fix there.
(In passing, clean up shaky grammar in the existing comments
for this function.)
Richard Guo
Discussion: https://postgr.es/m/CAMbWs4-krwk0Wbd6WdufMAupuou_Ua73ijQ4XQCr1Mb5BaVtKQ@mail.gmail.com
This commit is contained in:
parent
792213f2e9
commit
63e4f13d2a
|
@ -421,12 +421,33 @@ have_unsafe_outer_join_ref(PlannerInfo *root,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* paraminfo_get_equal_hashops
|
* paraminfo_get_equal_hashops
|
||||||
* Determine if param_info and innerrel's lateral_vars can be hashed.
|
* Determine if the clauses in param_info and innerrel's lateral_vars
|
||||||
* Returns true the hashing is possible, otherwise return false.
|
* can be hashed.
|
||||||
|
* Returns true if hashing is possible, otherwise false.
|
||||||
*
|
*
|
||||||
* Additionally we also collect the outer exprs and the hash operators for
|
* Additionally, on success we collect the outer expressions and the
|
||||||
* each parameter to innerrel. These set in 'param_exprs', 'operators' and
|
* appropriate equality operators for each hashable parameter to innerrel.
|
||||||
* 'binary_mode' when we return true.
|
* These are returned in parallel lists in *param_exprs and *operators.
|
||||||
|
* We also set *binary_mode to indicate whether strict binary matching is
|
||||||
|
* required.
|
||||||
|
*
|
||||||
|
* A complication is that innerrel's lateral_vars 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 contains 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 outerrel->relids.
|
||||||
|
* That fixes matters for the case of forward application of identity 3.
|
||||||
|
* If the identity was applied in the reverse direction, we will have
|
||||||
|
* innerrel's lateral_vars 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.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
paraminfo_get_equal_hashops(PlannerInfo *root, ParamPathInfo *param_info,
|
paraminfo_get_equal_hashops(PlannerInfo *root, ParamPathInfo *param_info,
|
||||||
|
@ -441,6 +462,7 @@ paraminfo_get_equal_hashops(PlannerInfo *root, ParamPathInfo *param_info,
|
||||||
*operators = NIL;
|
*operators = NIL;
|
||||||
*binary_mode = false;
|
*binary_mode = false;
|
||||||
|
|
||||||
|
/* Add join clauses from param_info to the hash key */
|
||||||
if (param_info != NULL)
|
if (param_info != NULL)
|
||||||
{
|
{
|
||||||
List *clauses = param_info->ppi_clauses;
|
List *clauses = param_info->ppi_clauses;
|
||||||
|
@ -510,7 +532,7 @@ paraminfo_get_equal_hashops(PlannerInfo *root, ParamPathInfo *param_info,
|
||||||
Node *expr = (Node *) lfirst(lc);
|
Node *expr = (Node *) lfirst(lc);
|
||||||
TypeCacheEntry *typentry;
|
TypeCacheEntry *typentry;
|
||||||
|
|
||||||
/* Reject if there are any volatile functions */
|
/* Reject if there are any volatile functions in PHVs */
|
||||||
if (contain_volatile_functions(expr))
|
if (contain_volatile_functions(expr))
|
||||||
{
|
{
|
||||||
list_free(*operators);
|
list_free(*operators);
|
||||||
|
@ -521,7 +543,7 @@ paraminfo_get_equal_hashops(PlannerInfo *root, ParamPathInfo *param_info,
|
||||||
typentry = lookup_type_cache(exprType(expr),
|
typentry = lookup_type_cache(exprType(expr),
|
||||||
TYPECACHE_HASH_PROC | TYPECACHE_EQ_OPR);
|
TYPECACHE_HASH_PROC | TYPECACHE_EQ_OPR);
|
||||||
|
|
||||||
/* can't use a memoize node without a valid hash equals operator */
|
/* can't use memoize without a valid hash proc and equals operator */
|
||||||
if (!OidIsValid(typentry->hash_proc) || !OidIsValid(typentry->eq_opr))
|
if (!OidIsValid(typentry->hash_proc) || !OidIsValid(typentry->eq_opr))
|
||||||
{
|
{
|
||||||
list_free(*operators);
|
list_free(*operators);
|
||||||
|
@ -529,6 +551,25 @@ paraminfo_get_equal_hashops(PlannerInfo *root, ParamPathInfo *param_info,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* OK, but adjust its nullingrels before adding it to result */
|
||||||
|
expr = copyObject(expr);
|
||||||
|
if (IsA(expr, Var))
|
||||||
|
{
|
||||||
|
Var *var = (Var *) expr;
|
||||||
|
|
||||||
|
var->varnullingrels = bms_intersect(var->varnullingrels,
|
||||||
|
outerrel->relids);
|
||||||
|
}
|
||||||
|
else if (IsA(expr, PlaceHolderVar))
|
||||||
|
{
|
||||||
|
PlaceHolderVar *phv = (PlaceHolderVar *) expr;
|
||||||
|
|
||||||
|
phv->phnullingrels = bms_intersect(phv->phnullingrels,
|
||||||
|
outerrel->relids);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Assert(false);
|
||||||
|
|
||||||
*operators = lappend_oid(*operators, typentry->eq_opr);
|
*operators = lappend_oid(*operators, typentry->eq_opr);
|
||||||
*param_exprs = lappend(*param_exprs, expr);
|
*param_exprs = lappend(*param_exprs, expr);
|
||||||
|
|
||||||
|
|
|
@ -2289,11 +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. Another case that can cause that to happen is explained
|
* side. Lateral references can create the same situation, as
|
||||||
* in the comments for process_subquery_nestloop_params. Not
|
* explained in the comments for process_subquery_nestloop_params
|
||||||
* checking this exactly is a bit grotty, but the work needed to
|
* and paraminfo_get_equal_hashops. Not checking this exactly is
|
||||||
* make things match up perfectly seems well out of proportion to
|
* a bit grotty, but the work needed to make things match up
|
||||||
* the value.
|
* 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,
|
||||||
|
|
|
@ -2607,6 +2607,27 @@ select * from int8_tbl t1
|
||||||
Filter: (q1 = t2.q1)
|
Filter: (q1 = t2.q1)
|
||||||
(8 rows)
|
(8 rows)
|
||||||
|
|
||||||
|
explain (costs off)
|
||||||
|
select * from onek t1
|
||||||
|
left join onek t2 on true
|
||||||
|
left join lateral
|
||||||
|
(select * from onek t3 where t3.two = t2.two offset 0) s
|
||||||
|
on t2.unique1 = 1;
|
||||||
|
QUERY PLAN
|
||||||
|
--------------------------------------------------
|
||||||
|
Nested Loop Left Join
|
||||||
|
-> Seq Scan on onek t1
|
||||||
|
-> Materialize
|
||||||
|
-> Nested Loop Left Join
|
||||||
|
Join Filter: (t2.unique1 = 1)
|
||||||
|
-> Seq Scan on onek t2
|
||||||
|
-> Memoize
|
||||||
|
Cache Key: t2.two
|
||||||
|
Cache Mode: binary
|
||||||
|
-> Seq Scan on onek t3
|
||||||
|
Filter: (two = t2.two)
|
||||||
|
(11 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
|
||||||
|
|
|
@ -521,6 +521,13 @@ select * from int8_tbl t1
|
||||||
(select * from int8_tbl t3 where t3.q1 = t2.q1 offset 0) s
|
(select * from int8_tbl t3 where t3.q1 = t2.q1 offset 0) s
|
||||||
on t2.q1 = 1;
|
on t2.q1 = 1;
|
||||||
|
|
||||||
|
explain (costs off)
|
||||||
|
select * from onek t1
|
||||||
|
left join onek t2 on true
|
||||||
|
left join lateral
|
||||||
|
(select * from onek t3 where t3.two = t2.two offset 0) s
|
||||||
|
on t2.unique1 = 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