Skip to content

Commit f348f16

Browse files
author
Commitfest Bot
committed
[PATCH]: ./v3-0001-fix-bug-18953-some-more.patch
1 parent 50fd428 commit f348f16

File tree

7 files changed

+337
-37
lines changed

7 files changed

+337
-37
lines changed

src/backend/optimizer/plan/createplan.c

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4344,6 +4344,7 @@ create_nestloop_plan(PlannerInfo *root,
43444344
NestLoop *join_plan;
43454345
Plan *outer_plan;
43464346
Plan *inner_plan;
4347+
Relids outerrelids;
43474348
List *tlist = build_path_tlist(root, &best_path->jpath.path);
43484349
List *joinrestrictclauses = best_path->jpath.joinrestrictinfo;
43494350
List *joinclauses;
@@ -4374,8 +4375,8 @@ create_nestloop_plan(PlannerInfo *root,
43744375
outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath, 0);
43754376

43764377
/* For a nestloop, include outer relids in curOuterRels for inner side */
4377-
root->curOuterRels = bms_union(root->curOuterRels,
4378-
best_path->jpath.outerjoinpath->parent->relids);
4378+
outerrelids = best_path->jpath.outerjoinpath->parent->relids;
4379+
root->curOuterRels = bms_union(root->curOuterRels, outerrelids);
43794380

43804381
inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath, 0);
43814382

@@ -4415,40 +4416,66 @@ create_nestloop_plan(PlannerInfo *root,
44154416
* node, and remove them from root->curOuterParams.
44164417
*/
44174418
nestParams = identify_current_nestloop_params(root,
4418-
best_path->jpath.outerjoinpath);
4419+
outerrelids,
4420+
PATH_REQ_OUTER((Path *) best_path));
44194421

44204422
/*
44214423
* While nestloop parameters that are Vars had better be available from
44224424
* the outer_plan already, there are edge cases where nestloop parameters
44234425
* that are PHVs won't be. In such cases we must add them to the
44244426
* outer_plan's tlist, since the executor's NestLoopParam machinery
44254427
* requires the params to be simple outer-Var references to that tlist.
4428+
* (This is cheating a little bit, because the outer path's required-outer
4429+
* relids might not be enough to allow evaluating such a PHV. But in
4430+
* practice, if we could have evaluated the PHV at the nestloop node, we
4431+
* can do so in the outer plan too.)
44264432
*/
44274433
outer_tlist = outer_plan->targetlist;
44284434
outer_parallel_safe = outer_plan->parallel_safe;
44294435
foreach(lc, nestParams)
44304436
{
44314437
NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
4438+
PlaceHolderVar *phv;
44324439
TargetEntry *tle;
44334440

44344441
if (IsA(nlp->paramval, Var))
44354442
continue; /* nothing to do for simple Vars */
4436-
if (tlist_member((Expr *) nlp->paramval, outer_tlist))
4443+
/* Otherwise it must be a PHV */
4444+
phv = castNode(PlaceHolderVar, nlp->paramval);
4445+
4446+
if (tlist_member((Expr *) phv, outer_tlist))
44374447
continue; /* already available */
44384448

4449+
/*
4450+
* It's possible that nestloop parameter PHVs selected to evaluate
4451+
* here contain references to surviving root->curOuterParams items
4452+
* (that is, they reference values that will be supplied by some
4453+
* higher-level nestloop). Those need to be converted to Params now.
4454+
*/
4455+
phv = (PlaceHolderVar *) replace_nestloop_params(root, (Node *) phv);
4456+
4457+
/*
4458+
* It's not actually necessary to update the NestLoopParam entry,
4459+
* because the two PHVs would be equal() anyway and thus setrefs.c
4460+
* would still replace the NestLoopParam's PHV with an outer-plan
4461+
* reference Var. (This is also why we needn't run
4462+
* replace_nestloop_params before checking tlist_member.) But we
4463+
* might as well do it for cleanliness.
4464+
*/
4465+
nlp->paramval = (Var *) phv;
4466+
44394467
/* Make a shallow copy of outer_tlist, if we didn't already */
44404468
if (outer_tlist == outer_plan->targetlist)
44414469
outer_tlist = list_copy(outer_tlist);
44424470
/* ... and add the needed expression */
4443-
tle = makeTargetEntry((Expr *) copyObject(nlp->paramval),
4471+
tle = makeTargetEntry((Expr *) copyObject(phv),
44444472
list_length(outer_tlist) + 1,
44454473
NULL,
44464474
true);
44474475
outer_tlist = lappend(outer_tlist, tle);
44484476
/* ... and track whether tlist is (still) parallel-safe */
44494477
if (outer_parallel_safe)
4450-
outer_parallel_safe = is_parallel_safe(root,
4451-
(Node *) nlp->paramval);
4478+
outer_parallel_safe = is_parallel_safe(root, (Node *) phv);
44524479
}
44534480
if (outer_tlist != outer_plan->targetlist)
44544481
outer_plan = change_plan_targetlist(outer_plan, outer_tlist,

src/backend/optimizer/util/paramassign.c

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -599,38 +599,31 @@ process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
599599
}
600600

601601
/*
602-
* Identify any NestLoopParams that should be supplied by a NestLoop plan
603-
* node with the specified lefthand input path. Remove them from the active
604-
* root->curOuterParams list and return them as the result list.
602+
* Identify any NestLoopParams that should be supplied by a NestLoop
603+
* plan node with the specified lefthand rels and required-outer rels.
604+
* Remove them from the active root->curOuterParams list and return
605+
* them as the result list.
605606
*
606-
* XXX Here we also hack up the returned Vars and PHVs so that they do not
607-
* contain nullingrel sets exceeding what is available from the outer side.
608-
* This is needed if we have applied outer join identity 3,
609-
* (A leftjoin B on (Pab)) leftjoin C on (Pb*c)
610-
* = A leftjoin (B leftjoin C on (Pbc)) on (Pab)
611-
* and C contains lateral references to B. It's still safe to apply the
612-
* identity, but the parser will have created those references in the form
613-
* "b*" (i.e., with varnullingrels listing the A/B join), while what we will
614-
* have available from the nestloop's outer side is just "b". We deal with
615-
* that here by stripping the nullingrels down to what is available from the
616-
* outer side according to leftrelids.
617-
*
618-
* That fixes matters for the case of forward application of identity 3.
619-
* If the identity was applied in the reverse direction, we will have
620-
* parameter Vars containing too few nullingrel bits rather than too many.
621-
* Currently, that causes no problems because setrefs.c applies only a
622-
* subset check to nullingrels in NestLoopParams, but we'd have to work
623-
* harder if we ever want to tighten that check. This is all pretty annoying
624-
* because it greatly weakens setrefs.c's cross-check, but the alternative
607+
* Vars and PHVs appearing in the result list must have nullingrel sets
608+
* that could validly appear in the lefthand rel's output. Ordinarily that
609+
* would be true already, but if we have applied outer join identity 3,
610+
* there could be more or fewer nullingrel bits in the nodes appearing in
611+
* curOuterParams than are in the nominal leftrelids. We deal with that by
612+
* forcing their nullingrel sets to include exactly the outer-join relids
613+
* that appear in leftrelids and can null the respective Var or PHV.
614+
* This fix is a bit ad-hoc and intellectually unsatisfactory, because it's
615+
* essentially jumping to the conclusion that we've placed evaluation of
616+
* the nestloop parameters correctly, and thus it defeats the intent of the
617+
* subsequent nullingrel cross-checks in setrefs.c. But the alternative
625618
* seems to be to generate multiple versions of each laterally-parameterized
626619
* subquery, which'd be unduly expensive.
627620
*/
628621
List *
629-
identify_current_nestloop_params(PlannerInfo *root, Path *leftpath)
622+
identify_current_nestloop_params(PlannerInfo *root,
623+
Relids leftrelids,
624+
Relids outerrelids)
630625
{
631626
List *result;
632-
Relids leftrelids = leftpath->parent->relids;
633-
Relids outerrelids = PATH_REQ_OUTER(leftpath);
634627
Relids allleftrelids;
635628
ListCell *cell;
636629

@@ -661,25 +654,58 @@ identify_current_nestloop_params(PlannerInfo *root, Path *leftpath)
661654
bms_is_member(nlp->paramval->varno, leftrelids))
662655
{
663656
Var *var = (Var *) nlp->paramval;
657+
RelOptInfo *rel = root->simple_rel_array[var->varno];
664658

665659
root->curOuterParams = foreach_delete_current(root->curOuterParams,
666660
cell);
667-
var->varnullingrels = bms_intersect(var->varnullingrels,
661+
var->varnullingrels = bms_intersect(rel->nulling_relids,
668662
leftrelids);
669663
result = lappend(result, nlp);
670664
}
671665
else if (IsA(nlp->paramval, PlaceHolderVar))
672666
{
673667
PlaceHolderVar *phv = (PlaceHolderVar *) nlp->paramval;
674-
Relids eval_at = find_placeholder_info(root, phv)->ph_eval_at;
668+
PlaceHolderInfo *phinfo = find_placeholder_info(root, phv);
669+
Relids eval_at = phinfo->ph_eval_at;
675670

676671
if (bms_is_subset(eval_at, allleftrelids) &&
677672
bms_overlap(eval_at, leftrelids))
678673
{
679674
root->curOuterParams = foreach_delete_current(root->curOuterParams,
680675
cell);
681-
phv->phnullingrels = bms_intersect(phv->phnullingrels,
682-
leftrelids);
676+
677+
/*
678+
* Deal with an edge case: if the PHV was pulled up out of a
679+
* subquery and it contains a subquery that was originally
680+
* pushed down from this query level, then that will still be
681+
* represented as a SubLink, because SS_process_sublinks won't
682+
* recurse into outer PHVs, so it didn't get transformed
683+
* during expression preprocessing in the subquery. We need a
684+
* version of the PHV that has a SubPlan, which we can get
685+
* from the current query level's placeholder_list. This is
686+
* quite grotty of course, but dealing with it earlier in the
687+
* handling of subplan params would be just as grotty, and it
688+
* might end up being a waste of cycles if we don't decide to
689+
* treat the PHV as a NestLoopParam. (Perhaps that whole
690+
* mechanism should be redesigned someday, but today is not
691+
* that day.)
692+
*/
693+
if (root->parse->hasSubLinks)
694+
{
695+
phv = copyObject(phinfo->ph_var);
696+
697+
/*
698+
* The ph_var will have empty nullingrels, but that
699+
* doesn't matter since we're about to overwrite
700+
* phv->phnullingrels. Other fields should be OK already.
701+
*/
702+
nlp->paramval = (Var *) phv;
703+
}
704+
705+
phv->phnullingrels =
706+
bms_intersect(get_placeholder_nulling_relids(root, phinfo),
707+
leftrelids);
708+
683709
result = lappend(result, nlp);
684710
}
685711
}

src/backend/optimizer/util/placeholder.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,3 +545,43 @@ contain_placeholder_references_walker(Node *node,
545545
return expression_tree_walker(node, contain_placeholder_references_walker,
546546
context);
547547
}
548+
549+
/*
550+
* Compute the set of outer-join relids that can null a placeholder.
551+
*
552+
* This is analogous to RelOptInfo.nulling_relids for Vars, but we compute it
553+
* on-the-fly rather than saving it somewhere. Currently the value is needed
554+
* at most once per query, so there's little value in doing otherwise. If it
555+
* ever gains more widespread use, perhaps we should cache the result in
556+
* PlaceHolderInfo.
557+
*/
558+
Relids
559+
get_placeholder_nulling_relids(PlannerInfo *root, PlaceHolderInfo *phinfo)
560+
{
561+
Relids result = NULL;
562+
int relid = -1;
563+
564+
/*
565+
* Form the union of all potential nulling OJs for each baserel included
566+
* in ph_eval_at.
567+
*/
568+
while ((relid = bms_next_member(phinfo->ph_eval_at, relid)) > 0)
569+
{
570+
RelOptInfo *rel = root->simple_rel_array[relid];
571+
572+
/* ignore the RTE_GROUP RTE */
573+
if (relid == root->group_rtindex)
574+
continue;
575+
576+
if (rel == NULL) /* must be an outer join */
577+
{
578+
Assert(bms_is_member(relid, root->outer_join_rels));
579+
continue;
580+
}
581+
result = bms_add_members(result, rel->nulling_relids);
582+
}
583+
584+
/* Now remove any OJs already included in ph_eval_at, and we're done. */
585+
result = bms_del_members(result, phinfo->ph_eval_at);
586+
return result;
587+
}

src/include/optimizer/paramassign.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ extern Param *replace_nestloop_param_placeholdervar(PlannerInfo *root,
3030
extern void process_subquery_nestloop_params(PlannerInfo *root,
3131
List *subplan_params);
3232
extern List *identify_current_nestloop_params(PlannerInfo *root,
33-
Path *leftpath);
33+
Relids leftrelids,
34+
Relids outerrelids);
3435
extern Param *generate_new_exec_param(PlannerInfo *root, Oid paramtype,
3536
int32 paramtypmod, Oid paramcollation);
3637
extern int assign_special_exec_param(PlannerInfo *root);

src/include/optimizer/placeholder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,7 @@ extern void add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
3030
SpecialJoinInfo *sjinfo);
3131
extern bool contain_placeholder_references_to(PlannerInfo *root, Node *clause,
3232
int relid);
33+
extern Relids get_placeholder_nulling_relids(PlannerInfo *root,
34+
PlaceHolderInfo *phinfo);
3335

3436
#endif /* PLACEHOLDER_H */

0 commit comments

Comments
 (0)