8
8
*
9
9
*
10
10
* IDENTIFICATION
11
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.99 2003/03/10 03:53:49 tgl Exp $
11
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.100 2003/03/22 01:49:38 tgl Exp $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
26
26
#include "optimizer/plancat.h"
27
27
#include "optimizer/planner.h"
28
28
#include "optimizer/prep.h"
29
+ #include "optimizer/var.h"
29
30
#include "parser/parsetree.h"
31
+ #include "parser/parse_clause.h"
30
32
#include "rewrite/rewriteManip.h"
31
33
32
34
@@ -49,6 +51,7 @@ static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
49
51
List * initial_rels );
50
52
static bool subquery_is_pushdown_safe (Query * subquery , Query * topquery );
51
53
static bool recurse_pushdown_safe (Node * setOp , Query * topquery );
54
+ static bool qual_is_pushdown_safe (Query * subquery , Index rti , Node * qual );
52
55
static void subquery_push_qual (Query * subquery , Index rti , Node * qual );
53
56
static void recurse_push_qual (Node * setOp , Query * topquery ,
54
57
Index rti , Node * qual );
@@ -305,16 +308,14 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
305
308
*
306
309
* There are several cases where we cannot push down clauses.
307
310
* Restrictions involving the subquery are checked by
308
- * subquery_is_pushdown_safe(). Also, we do not push down clauses
309
- * that contain subselects, mainly because I'm not sure it will work
310
- * correctly (the subplan hasn't yet transformed sublinks to
311
- * subselects).
311
+ * subquery_is_pushdown_safe(). Restrictions on individual clauses are
312
+ * checked by qual_is_pushdown_safe().
312
313
*
313
314
* Non-pushed-down clauses will get evaluated as qpquals of the
314
315
* SubqueryScan node.
315
316
*
316
317
* XXX Are there any cases where we want to make a policy decision not to
317
- * push down, because it'd result in a worse plan?
318
+ * push down a pushable qual , because it'd result in a worse plan?
318
319
*/
319
320
if (rel -> baserestrictinfo != NIL &&
320
321
subquery_is_pushdown_safe (subquery , subquery ))
@@ -328,15 +329,15 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
328
329
RestrictInfo * rinfo = (RestrictInfo * ) lfirst (lst );
329
330
Node * clause = (Node * ) rinfo -> clause ;
330
331
331
- if (contain_subplans ( clause ))
332
+ if (qual_is_pushdown_safe ( subquery , rti , clause ))
332
333
{
333
- /* Keep it in the upper query */
334
- upperrestrictlist = lappend ( upperrestrictlist , rinfo );
334
+ /* Push it down */
335
+ subquery_push_qual ( subquery , rti , clause );
335
336
}
336
337
else
337
338
{
338
- /* Push it down */
339
- subquery_push_qual ( subquery , rti , clause );
339
+ /* Keep it in the upper query */
340
+ upperrestrictlist = lappend ( upperrestrictlist , rinfo );
340
341
}
341
342
}
342
343
rel -> baserestrictinfo = upperrestrictlist ;
@@ -527,21 +528,10 @@ make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
527
528
*
528
529
* Conditions checked here:
529
530
*
530
- * 1. If the subquery has a LIMIT clause or a DISTINCT ON clause, we must
531
- * not push down any quals, since that could change the set of rows
532
- * returned. (Actually, we could push down quals into a DISTINCT ON
533
- * subquery if they refer only to DISTINCT-ed output columns, but
534
- * checking that seems more work than it's worth. In any case, a
535
- * plain DISTINCT is safe to push down past.)
536
- *
537
- * 2. If the subquery has any functions returning sets in its target list,
538
- * we do not push down any quals, since the quals
539
- * might refer to those tlist items, which would mean we'd introduce
540
- * functions-returning-sets into the subquery's WHERE/HAVING quals.
541
- * (It'd be sufficient to not push down quals that refer to those
542
- * particular tlist items, but that's much clumsier to check.)
531
+ * 1. If the subquery has a LIMIT clause, we must not push down any quals,
532
+ * since that could change the set of rows returned.
543
533
*
544
- * 3 . If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
534
+ * 2 . If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
545
535
* quals into it, because that would change the results. For subqueries
546
536
* using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can push the quals
547
537
* into each component query, so long as all the component queries share
@@ -554,11 +544,8 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery)
554
544
{
555
545
SetOperationStmt * topop ;
556
546
557
- /* Check points 1 and 2 */
558
- if (subquery -> limitOffset != NULL ||
559
- subquery -> limitCount != NULL ||
560
- has_distinct_on_clause (subquery ) ||
561
- expression_returns_set ((Node * ) subquery -> targetList ))
547
+ /* Check point 1 */
548
+ if (subquery -> limitOffset != NULL || subquery -> limitCount != NULL )
562
549
return false;
563
550
564
551
/* Are we at top level, or looking at a setop component? */
@@ -622,6 +609,89 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
622
609
return true;
623
610
}
624
611
612
+ /*
613
+ * qual_is_pushdown_safe - is a particular qual safe to push down?
614
+ *
615
+ * qual is a restriction clause applying to the given subquery (whose RTE
616
+ * has index rti in the parent query).
617
+ *
618
+ * Conditions checked here:
619
+ *
620
+ * 1. The qual must not contain any subselects (mainly because I'm not sure
621
+ * it will work correctly: sublinks will already have been transformed into
622
+ * subplans in the qual, but not in the subquery).
623
+ *
624
+ * 2. If the subquery uses DISTINCT ON, we must not push down any quals that
625
+ * refer to non-DISTINCT output columns, because that could change the set
626
+ * of rows returned. This condition is vacuous for DISTINCT, because then
627
+ * there are no non-DISTINCT output columns, but unfortunately it's fairly
628
+ * expensive to tell the difference between DISTINCT and DISTINCT ON in the
629
+ * parsetree representation. It's cheaper to just make sure all the Vars
630
+ * in the qual refer to DISTINCT columns.
631
+ *
632
+ * 3. We must not push down any quals that refer to subselect outputs that
633
+ * return sets, else we'd introduce functions-returning-sets into the
634
+ * subquery's WHERE/HAVING quals.
635
+ */
636
+ static bool
637
+ qual_is_pushdown_safe (Query * subquery , Index rti , Node * qual )
638
+ {
639
+ bool safe = true;
640
+ List * vars ;
641
+ List * l ;
642
+ Bitmapset * tested = NULL ;
643
+
644
+ /* Refuse subselects (point 1) */
645
+ if (contain_subplans (qual ))
646
+ return false;
647
+
648
+ /*
649
+ * Examine all Vars used in clause; since it's a restriction clause,
650
+ * all such Vars must refer to subselect output columns.
651
+ */
652
+ vars = pull_var_clause (qual , false);
653
+ foreach (l , vars )
654
+ {
655
+ Var * var = (Var * ) lfirst (l );
656
+ TargetEntry * tle ;
657
+
658
+ Assert (var -> varno == rti );
659
+ /*
660
+ * We use a bitmapset to avoid testing the same attno more than
661
+ * once. (NB: this only works because subquery outputs can't
662
+ * have negative attnos.)
663
+ */
664
+ if (bms_is_member (var -> varattno , tested ))
665
+ continue ;
666
+ tested = bms_add_member (tested , var -> varattno );
667
+
668
+ tle = (TargetEntry * ) nth (var -> varattno - 1 , subquery -> targetList );
669
+ Assert (tle -> resdom -> resno == var -> varattno );
670
+ Assert (!tle -> resdom -> resjunk );
671
+
672
+ /* If subquery uses DISTINCT or DISTINCT ON, check point 2 */
673
+ if (subquery -> distinctClause != NIL &&
674
+ !targetIsInSortList (tle , subquery -> distinctClause ))
675
+ {
676
+ /* non-DISTINCT column, so fail */
677
+ safe = false;
678
+ break ;
679
+ }
680
+
681
+ /* Refuse functions returning sets (point 3) */
682
+ if (expression_returns_set ((Node * ) tle -> expr ))
683
+ {
684
+ safe = false;
685
+ break ;
686
+ }
687
+ }
688
+
689
+ freeList (vars );
690
+ bms_free (tested );
691
+
692
+ return safe ;
693
+ }
694
+
625
695
/*
626
696
* subquery_push_qual - push down a qual that we have determined is safe
627
697
*/
0 commit comments