@@ -421,12 +421,33 @@ have_unsafe_outer_join_ref(PlannerInfo *root,
421421
422422/*
423423 * paraminfo_get_equal_hashops
424- * Determine if param_info and innerrel's lateral_vars can be hashed.
425- * Returns true the hashing is possible, otherwise return false.
424+ * Determine if the clauses in param_info and innerrel's lateral_vars
425+ * can be hashed.
426+ * Returns true if hashing is possible, otherwise false.
426427 *
427- * Additionally we also collect the outer exprs and the hash operators for
428- * each parameter to innerrel. These set in 'param_exprs', 'operators' and
429- * 'binary_mode' when we return true.
428+ * Additionally, on success we collect the outer expressions and the
429+ * appropriate equality operators for each hashable parameter to innerrel.
430+ * These are returned in parallel lists in *param_exprs and *operators.
431+ * We also set *binary_mode to indicate whether strict binary matching is
432+ * required.
433+ *
434+ * A complication is that innerrel's lateral_vars may contain nullingrel
435+ * markers that need adjustment. This occurs if we have applied outer join
436+ * identity 3,
437+ * (A leftjoin B on (Pab)) leftjoin C on (Pb*c)
438+ * = A leftjoin (B leftjoin C on (Pbc)) on (Pab)
439+ * and C contains lateral references to B. It's still safe to apply the
440+ * identity, but the parser will have created those references in the form
441+ * "b*" (i.e., with varnullingrels listing the A/B join), while what we will
442+ * have available from the nestloop's outer side is just "b". We deal with
443+ * that here by stripping the nullingrels down to what is available from the
444+ * outer side according to outerrel->relids.
445+ * That fixes matters for the case of forward application of identity 3.
446+ * If the identity was applied in the reverse direction, we will have
447+ * innerrel's lateral_vars containing too few nullingrel bits rather than
448+ * too many. Currently, that causes no problems because setrefs.c applies
449+ * only a subset check to nullingrels in NestLoopParams, but we'd have to
450+ * work harder if we ever want to tighten that check.
430451 */
431452static bool
432453paraminfo_get_equal_hashops (PlannerInfo * root , ParamPathInfo * param_info ,
@@ -441,6 +462,7 @@ paraminfo_get_equal_hashops(PlannerInfo *root, ParamPathInfo *param_info,
441462 * operators = NIL ;
442463 * binary_mode = false;
443464
465+ /* Add join clauses from param_info to the hash key */
444466 if (param_info != NULL )
445467 {
446468 List * clauses = param_info -> ppi_clauses ;
@@ -510,7 +532,7 @@ paraminfo_get_equal_hashops(PlannerInfo *root, ParamPathInfo *param_info,
510532 Node * expr = (Node * ) lfirst (lc );
511533 TypeCacheEntry * typentry ;
512534
513- /* Reject if there are any volatile functions */
535+ /* Reject if there are any volatile functions in PHVs */
514536 if (contain_volatile_functions (expr ))
515537 {
516538 list_free (* operators );
@@ -521,14 +543,33 @@ paraminfo_get_equal_hashops(PlannerInfo *root, ParamPathInfo *param_info,
521543 typentry = lookup_type_cache (exprType (expr ),
522544 TYPECACHE_HASH_PROC | TYPECACHE_EQ_OPR );
523545
524- /* can't use a memoize node without a valid hash equals operator */
546+ /* can't use memoize without a valid hash proc and equals operator */
525547 if (!OidIsValid (typentry -> hash_proc ) || !OidIsValid (typentry -> eq_opr ))
526548 {
527549 list_free (* operators );
528550 list_free (* param_exprs );
529551 return false;
530552 }
531553
554+ /* OK, but adjust its nullingrels before adding it to result */
555+ expr = copyObject (expr );
556+ if (IsA (expr , Var ))
557+ {
558+ Var * var = (Var * ) expr ;
559+
560+ var -> varnullingrels = bms_intersect (var -> varnullingrels ,
561+ outerrel -> relids );
562+ }
563+ else if (IsA (expr , PlaceHolderVar ))
564+ {
565+ PlaceHolderVar * phv = (PlaceHolderVar * ) expr ;
566+
567+ phv -> phnullingrels = bms_intersect (phv -> phnullingrels ,
568+ outerrel -> relids );
569+ }
570+ else
571+ Assert (false);
572+
532573 * operators = lappend_oid (* operators , typentry -> eq_opr );
533574 * param_exprs = lappend (* param_exprs , expr );
534575
0 commit comments