@@ -163,13 +163,12 @@ static pushdown_safe_type qual_is_pushdown_safe(Query *subquery, Index rti,
163163 RestrictInfo * rinfo ,
164164 pushdown_safety_info * safetyInfo );
165165static void subquery_push_qual (Query * subquery ,
166- RangeTblEntry * rte , Index rti , Node * qual );
166+ RangeTblEntry * rte , Index rti , Node * qual , int sublevels_up );
167167static void recurse_push_qual (Node * setOp , Query * topquery ,
168- RangeTblEntry * rte , Index rti , Node * qual );
168+ RangeTblEntry * rte , Index rti , Node * qual , int sublevels_up );
169169static void remove_unused_subquery_outputs (Query * subquery , RelOptInfo * rel ,
170170 Bitmapset * extra_used_attrs );
171171
172-
173172/*
174173 * make_one_rel
175174 * Finds all possible access paths for executing a query, returning a
@@ -2674,6 +2673,11 @@ check_and_push_window_quals(Query *subquery, Node *clause,
26742673 * So the paths made here will be parameterized if the subquery contains
26752674 * LATERAL references, otherwise not. As long as that's true, there's no need
26762675 * for a separate set_subquery_size phase: just make the paths right away.
2676+ *
2677+ * (If a subquery is LATERAL, though, we do push down join clauses that refer
2678+ * to relations that the subquery already references laterally. Pushing down
2679+ * such quals won't make the subquery any more lateral, so there's no reason
2680+ * not to.)
26772681 */
26782682static void
26792683set_subquery_pathlist (PlannerInfo * root , RelOptInfo * rel ,
@@ -2689,6 +2693,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
26892693 Bitmapset * run_cond_attrs = NULL ;
26902694 ListCell * lc ;
26912695 char * plan_name ;
2696+ List * pushed_down_ec_joins = NIL ;
26922697
26932698 /*
26942699 * Must copy the Query so that planning doesn't mess up the RTE contents
@@ -2699,8 +2704,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
26992704
27002705 /*
27012706 * If it's a LATERAL subquery, it might contain some Vars of the current
2702- * query level, requiring it to be treated as parameterized, even though
2703- * we don't support pushing down join quals into subqueries.
2707+ * query level, requiring it to be treated as parameterized.
27042708 */
27052709 required_outer = rel -> lateral_relids ;
27062710
@@ -2739,65 +2743,140 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
27392743 * pseudoconstant clauses; better to have the gating node above the
27402744 * subquery.
27412745 *
2746+ * Join clauses are only pushed down, if the subquery is LATERAL, and
2747+ * the join clause only refers to relations that the subquery already
2748+ * depends on. It might be useful to push down other join clauses, too,
2749+ * but then we would need to plan the subquery multiple times, to create
2750+ * parameterized paths, which seems too expensive.
2751+ *
27422752 * Non-pushed-down clauses will get evaluated as qpquals of the
27432753 * SubqueryScan node.
27442754 *
27452755 * XXX Are there any cases where we want to make a policy decision not to
27462756 * push down a pushable qual, because it'd result in a worse plan?
27472757 */
2748- if (rel -> baserestrictinfo != NIL &&
2758+ if ((rel -> baserestrictinfo != NIL ||
2759+ (!bms_is_empty (required_outer ) && (rel -> joininfo || rel -> has_eclass_joins ))) &&
27492760 subquery_is_pushdown_safe (subquery , subquery , & safetyInfo ))
27502761 {
27512762 /* OK to consider pushing down individual quals */
2752- List * upperrestrictlist = NIL ;
27532763 ListCell * l ;
2764+ Bitmapset * available_relids ;
27542765
2755- foreach ( l , rel -> baserestrictinfo )
2766+ if ( rel -> baserestrictinfo )
27562767 {
2757- RestrictInfo * rinfo = (RestrictInfo * ) lfirst (l );
2758- Node * clause = (Node * ) rinfo -> clause ;
2768+ List * upperrestrictlist = NIL ;
27592769
2760- if (rinfo -> pseudoconstant )
2761- {
2762- upperrestrictlist = lappend (upperrestrictlist , rinfo );
2763- continue ;
2764- }
27652770
2766- switch ( qual_is_pushdown_safe ( subquery , rti , rinfo , & safetyInfo ) )
2771+ foreach ( l , rel -> baserestrictinfo )
27672772 {
2768- case PUSHDOWN_SAFE :
2769- /* Push it down */
2770- subquery_push_qual (subquery , rte , rti , clause );
2771- break ;
2773+ RestrictInfo * rinfo = (RestrictInfo * ) lfirst (l );
2774+ Node * clause = (Node * ) rinfo -> clause ;
27722775
2773- case PUSHDOWN_WINDOWCLAUSE_RUNCOND :
2776+ if (rinfo -> pseudoconstant )
2777+ {
2778+ upperrestrictlist = lappend (upperrestrictlist , rinfo );
2779+ continue ;
2780+ }
2781+
2782+ switch (qual_is_pushdown_safe (subquery , rti , rinfo , & safetyInfo ))
2783+ {
2784+ case PUSHDOWN_SAFE :
2785+ /* Push it down */
2786+ subquery_push_qual (subquery , rte , rti , clause , 0 );
2787+ break ;
2788+
2789+ case PUSHDOWN_WINDOWCLAUSE_RUNCOND :
27742790
2775- /*
2776- * Since we can't push the qual down into the subquery,
2777- * check if it happens to reference a window function. If
2778- * so then it might be useful to use for the WindowAgg's
2779- * runCondition.
2780- */
2781- if (!subquery -> hasWindowFuncs ||
2782- check_and_push_window_quals (subquery , clause ,
2783- & run_cond_attrs ))
2784- {
27852791 /*
2786- * subquery has no window funcs or the clause is not a
2787- * suitable window run condition qual or it is, but
2788- * the original must also be kept in the upper query.
2789- */
2792+ * Since we can't push the qual down into the subquery,
2793+ * check if it happens to reference a window function. If
2794+ * so then it might be useful to use for the WindowAgg's
2795+ * runCondition.
2796+ */
2797+ if (!subquery -> hasWindowFuncs ||
2798+ check_and_push_window_quals (subquery , clause ,
2799+ & run_cond_attrs ))
2800+ {
2801+ /*
2802+ * subquery has no window funcs or the clause is not a
2803+ * suitable window run condition qual or it is, but
2804+ * the original must also be kept in the upper query.
2805+ */
2806+ upperrestrictlist = lappend (upperrestrictlist , rinfo );
2807+ }
2808+ break ;
2809+
2810+ case PUSHDOWN_UNSAFE :
2811+ /* Keep it in the upper query */
27902812 upperrestrictlist = lappend (upperrestrictlist , rinfo );
2813+ break ;
2814+ }
2815+ }
2816+ }
2817+
2818+ /*
2819+ * Push down join quals, as well. But only for LATERAL, and only for those
2820+ * relations that are "required" anyway.
2821+ */
2822+ if (!bms_is_empty (required_outer ))
2823+ {
2824+ available_relids = bms_copy (required_outer );
2825+ available_relids = bms_add_member (available_relids , rti );
2826+
2827+ if (rel -> joininfo )
2828+ {
2829+ ListCell * lc ;
2830+ List * upperjoinlist = NIL ;
2831+
2832+ foreach (lc , rel -> joininfo )
2833+ {
2834+ RestrictInfo * rinfo = (RestrictInfo * ) lfirst (lc );
2835+ Node * clause = (Node * ) rinfo -> clause ;
2836+
2837+ if (!rinfo -> pseudoconstant &&
2838+ bms_is_subset (rinfo -> required_relids , available_relids ) &&
2839+ qual_is_pushdown_safe (subquery , rti , clause , & safetyInfo ))
2840+ {
2841+ /* Push it down */
2842+ subquery_push_qual (subquery , rte , rti , clause , 0 );
2843+ }
2844+ else
2845+ {
2846+ /* Keep it in the upper query */
2847+ upperjoinlist = lappend (upperjoinlist , rinfo );
27912848 }
2792- break ;
2849+ }
2850+ rel -> joininfo = upperjoinlist ;
2851+ }
27932852
2794- case PUSHDOWN_UNSAFE :
2795- upperrestrictlist = lappend (upperrestrictlist , rinfo );
2796- break ;
2853+ if (rel -> has_eclass_joins )
2854+ {
2855+ List * clauses ;
2856+
2857+ clauses = generate_join_implied_equalities (root ,
2858+ available_relids ,
2859+ required_outer ,
2860+ rel ,
2861+ NULL );
2862+
2863+ foreach (lc , clauses )
2864+ {
2865+ RestrictInfo * rinfo = (RestrictInfo * ) lfirst (lc );
2866+ Node * clause = (Node * ) rinfo -> clause ;
2867+
2868+ if (!rinfo -> pseudoconstant &&
2869+ qual_is_pushdown_safe (subquery , rti , clause , & safetyInfo ))
2870+ {
2871+ /* Push it down */
2872+ Assert (bms_is_subset (rinfo -> required_relids , available_relids ));
2873+ subquery_push_qual (subquery , rte , rti , clause , 0 );
2874+
2875+ pushed_down_ec_joins = lappend (pushed_down_ec_joins , clause );
2876+ }
2877+ }
27972878 }
27982879 }
2799- rel -> baserestrictinfo = upperrestrictlist ;
2800- /* We don't bother recomputing baserestrict_min_security */
28012880 }
28022881
28032882 pfree (safetyInfo .unsafeFlags );
@@ -2908,7 +2987,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
29082987 add_path (rel , (Path * )
29092988 create_subqueryscan_path (root , rel , subpath ,
29102989 trivial_pathtarget ,
2911- pathkeys , required_outer ));
2990+ pathkeys , required_outer , pushed_down_ec_joins ));
29122991 }
29132992
29142993 /* If outer rel allows parallelism, do same for partial paths. */
@@ -2935,7 +3014,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
29353014 create_subqueryscan_path (root , rel , subpath ,
29363015 trivial_pathtarget ,
29373016 pathkeys ,
2938- required_outer ));
3017+ required_outer , pushed_down_ec_joins ));
29393018 }
29403019 }
29413020}
@@ -4505,6 +4584,13 @@ qual_is_pushdown_safe(Query *subquery, Index rti, RestrictInfo *rinfo,
45054584 break ;
45064585 }
45074586
4587+ /*
4588+ * In a restriction clause, all Vars must refer to subselect output
4589+ * columns, but join quals will contain Vars referring to other relations.
4590+ */
4591+ if (var -> varno != rti )
4592+ continue ;
4593+
45084594 /* Subqueries have no system columns */
45094595 Assert (var -> varattno >= 0 );
45104596
@@ -4543,26 +4629,32 @@ qual_is_pushdown_safe(Query *subquery, Index rti, RestrictInfo *rinfo,
45434629 * subquery_push_qual - push down a qual that we have determined is safe
45444630 */
45454631static void
4546- subquery_push_qual (Query * subquery , RangeTblEntry * rte , Index rti , Node * qual )
4632+ subquery_push_qual (Query * subquery , RangeTblEntry * rte , Index rti , Node * qual , int sublevels_up )
45474633{
45484634 if (subquery -> setOperations != NULL )
45494635 {
45504636 /* Recurse to push it separately to each component query */
45514637 recurse_push_qual (subquery -> setOperations , subquery ,
4552- rte , rti , qual );
4638+ rte , rti , qual , sublevels_up );
45534639 }
45544640 else
45554641 {
45564642 /*
45574643 * We need to replace Vars in the qual (which must refer to outputs of
45584644 * the subquery) with copies of the subquery's targetlist expressions.
45594645 * Note that at this point, any uplevel Vars in the qual should have
4560- * been replaced with Params, so they need no work.
4646+ * been replaced with Params, so they need no work. But in a join qual,
4647+ * there can be Vars referring to other relations at the same level.
4648+ * We need to increment varlevelsup of those, so that when the qual is
4649+ * pushed down, they refer to the parent query.
45614650 *
45624651 * This step also ensures that when we are pushing into a setop tree,
45634652 * each component query gets its own copy of the qual.
45644653 */
4565- qual = ReplaceVarsFromTargetList (qual , rti , 0 , rte ,
4654+ qual = copyObject (qual );
4655+ IncrementVarSublevelsUp (qual , sublevels_up + 1 , 0 );
4656+
4657+ qual = ReplaceVarsFromTargetList (qual , rti , sublevels_up + 1 , rte ,
45664658 subquery -> targetList ,
45674659 subquery -> resultRelation ,
45684660 REPLACEVARS_REPORT_ERROR , 0 ,
@@ -4592,7 +4684,7 @@ subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual)
45924684 */
45934685static void
45944686recurse_push_qual (Node * setOp , Query * topquery ,
4595- RangeTblEntry * rte , Index rti , Node * qual )
4687+ RangeTblEntry * rte , Index rti , Node * qual , int sublevels_up )
45964688{
45974689 if (IsA (setOp , RangeTblRef ))
45984690 {
@@ -4601,14 +4693,14 @@ recurse_push_qual(Node *setOp, Query *topquery,
46014693 Query * subquery = subrte -> subquery ;
46024694
46034695 Assert (subquery != NULL );
4604- subquery_push_qual (subquery , rte , rti , qual );
4696+ subquery_push_qual (subquery , rte , rti , qual , sublevels_up + 1 );
46054697 }
46064698 else if (IsA (setOp , SetOperationStmt ))
46074699 {
46084700 SetOperationStmt * op = (SetOperationStmt * ) setOp ;
46094701
4610- recurse_push_qual (op -> larg , topquery , rte , rti , qual );
4611- recurse_push_qual (op -> rarg , topquery , rte , rti , qual );
4702+ recurse_push_qual (op -> larg , topquery , rte , rti , qual , sublevels_up );
4703+ recurse_push_qual (op -> rarg , topquery , rte , rti , qual , sublevels_up );
46124704 }
46134705 else
46144706 {
0 commit comments