@@ -691,6 +691,7 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
691
691
Relids joinrelids ;
692
692
SpecialJoinInfo * sjinfo ;
693
693
bool reversed ;
694
+ List * pushed_down_joins = NIL ;
694
695
SpecialJoinInfo sjinfo_data ;
695
696
RelOptInfo * joinrel ;
696
697
List * restrictlist ;
@@ -710,9 +711,12 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
710
711
return NULL ;
711
712
}
712
713
713
- /* If we have an outer join, add its RTI to form the canonical relids. */
714
- if (sjinfo && sjinfo -> ojrelid != 0 )
715
- joinrelids = bms_add_member (joinrelids , sjinfo -> ojrelid );
714
+ /*
715
+ * Add outer join relid(s) to form the canonical relids. Any added outer
716
+ * joins besides sjinfo itself are appended to pushed_down_joins.
717
+ */
718
+ joinrelids = add_outer_joins_to_relids (root , joinrelids , sjinfo ,
719
+ & pushed_down_joins );
716
720
717
721
/* Swap rels if needed to match the join info. */
718
722
if (reversed )
@@ -740,7 +744,8 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
740
744
sjinfo -> ojrelid = 0 ;
741
745
sjinfo -> commute_above_l = NULL ;
742
746
sjinfo -> commute_above_r = NULL ;
743
- sjinfo -> commute_below = NULL ;
747
+ sjinfo -> commute_below_l = NULL ;
748
+ sjinfo -> commute_below_r = NULL ;
744
749
/* we don't bother trying to make the remaining fields valid */
745
750
sjinfo -> lhs_strict = false;
746
751
sjinfo -> semi_can_btree = false;
@@ -753,7 +758,8 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
753
758
* Find or build the join RelOptInfo, and compute the restrictlist that
754
759
* goes with this particular joining.
755
760
*/
756
- joinrel = build_join_rel (root , joinrelids , rel1 , rel2 , sjinfo ,
761
+ joinrel = build_join_rel (root , joinrelids , rel1 , rel2 ,
762
+ sjinfo , pushed_down_joins ,
757
763
& restrictlist );
758
764
759
765
/*
@@ -775,6 +781,108 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
775
781
return joinrel ;
776
782
}
777
783
784
+ /*
785
+ * add_outer_joins_to_relids
786
+ * Add relids to input_relids to represent any outer joins that will be
787
+ * calculated at this join.
788
+ *
789
+ * input_relids is the union of the relid sets of the two input relations.
790
+ * Note that we modify this in-place and return it; caller must bms_copy()
791
+ * it first, if a separate value is desired.
792
+ *
793
+ * sjinfo represents the join being performed.
794
+ *
795
+ * If the current join completes the calculation of any outer joins that
796
+ * have been pushed down per outer-join identity 3, those relids will be
797
+ * added to the result along with sjinfo's own relid. If pushed_down_joins
798
+ * is not NULL, then also the SpecialJoinInfos for such added outer joins will
799
+ * be appended to *pushed_down_joins (so caller must initialize it to NIL).
800
+ */
801
+ Relids
802
+ add_outer_joins_to_relids (PlannerInfo * root , Relids input_relids ,
803
+ SpecialJoinInfo * sjinfo ,
804
+ List * * pushed_down_joins )
805
+ {
806
+ /* Nothing to do if this isn't an outer join with an assigned relid. */
807
+ if (sjinfo == NULL || sjinfo -> ojrelid == 0 )
808
+ return input_relids ;
809
+
810
+ /*
811
+ * If it's not a left join, we have no rules that would permit executing
812
+ * it in non-syntactic order, so just form the syntactic relid set. (This
813
+ * is just a quick-exit test; we'd come to the same conclusion anyway,
814
+ * since its commute_below_l and commute_above_l sets must be empty.)
815
+ */
816
+ if (sjinfo -> jointype != JOIN_LEFT )
817
+ return bms_add_member (input_relids , sjinfo -> ojrelid );
818
+
819
+ /*
820
+ * We cannot add the OJ relid if this join has been pushed into the RHS of
821
+ * a syntactically-lower left join per OJ identity 3. (If it has, then we
822
+ * cannot claim that its outputs represent the final state of its RHS.)
823
+ * There will not be any other OJs that can be added either, so we're
824
+ * done.
825
+ */
826
+ if (!bms_is_subset (sjinfo -> commute_below_l , input_relids ))
827
+ return input_relids ;
828
+
829
+ /* OK to add OJ's own relid */
830
+ input_relids = bms_add_member (input_relids , sjinfo -> ojrelid );
831
+
832
+ /*
833
+ * Contrariwise, if we are now forming the final result of such a commuted
834
+ * pair of OJs, it's time to add the relid(s) of the pushed-down join(s).
835
+ * We can skip this if this join was never a candidate to be pushed up.
836
+ */
837
+ if (sjinfo -> commute_above_l )
838
+ {
839
+ Relids commute_above_rels = bms_copy (sjinfo -> commute_above_l );
840
+ ListCell * lc ;
841
+
842
+ /*
843
+ * The current join could complete the nulling of more than one
844
+ * pushed-down join, so we have to examine all the SpecialJoinInfos.
845
+ * Because join_info_list was built in bottom-up order, it's
846
+ * sufficient to traverse it once: an ojrelid we add in one loop
847
+ * iteration would not have affected decisions of earlier iterations.
848
+ */
849
+ foreach (lc , root -> join_info_list )
850
+ {
851
+ SpecialJoinInfo * othersj = (SpecialJoinInfo * ) lfirst (lc );
852
+
853
+ if (othersj == sjinfo ||
854
+ othersj -> ojrelid == 0 || othersj -> jointype != JOIN_LEFT )
855
+ continue ; /* definitely not interesting */
856
+
857
+ if (!bms_is_member (othersj -> ojrelid , commute_above_rels ))
858
+ continue ;
859
+
860
+ /* Add it if not already present but conditions now satisfied */
861
+ if (!bms_is_member (othersj -> ojrelid , input_relids ) &&
862
+ bms_is_subset (othersj -> min_lefthand , input_relids ) &&
863
+ bms_is_subset (othersj -> min_righthand , input_relids ) &&
864
+ bms_is_subset (othersj -> commute_below_l , input_relids ))
865
+ {
866
+ input_relids = bms_add_member (input_relids , othersj -> ojrelid );
867
+ /* report such pushed down outer joins, if asked */
868
+ if (pushed_down_joins != NULL )
869
+ * pushed_down_joins = lappend (* pushed_down_joins , othersj );
870
+
871
+ /*
872
+ * We must also check any joins that othersj potentially
873
+ * commutes with. They likewise must appear later in
874
+ * join_info_list than othersj itself, so we can visit them
875
+ * later in this loop.
876
+ */
877
+ commute_above_rels = bms_add_members (commute_above_rels ,
878
+ othersj -> commute_above_l );
879
+ }
880
+ }
881
+ }
882
+
883
+ return input_relids ;
884
+ }
885
+
778
886
/*
779
887
* populate_joinrel_with_paths
780
888
* Add paths to the given joinrel for given pair of joining relations. The
@@ -1534,9 +1642,8 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
1534
1642
1535
1643
/* Build correct join relids for child join */
1536
1644
child_joinrelids = bms_union (child_rel1 -> relids , child_rel2 -> relids );
1537
- if (child_sjinfo -> ojrelid != 0 )
1538
- child_joinrelids = bms_add_member (child_joinrelids ,
1539
- child_sjinfo -> ojrelid );
1645
+ child_joinrelids = add_outer_joins_to_relids (root , child_joinrelids ,
1646
+ child_sjinfo , NULL );
1540
1647
1541
1648
/* Find the AppendRelInfo structures */
1542
1649
appinfos = find_appinfos_by_relids (root , child_joinrelids , & nappinfos );
0 commit comments