@@ -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 */
628621List *
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 }
0 commit comments