8
8
*
9
9
*
10
10
* IDENTIFICATION
11
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.86 2002/06/20 20:29:29 momjian Exp $
11
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.87 2002/08/29 16:03:48 tgl Exp $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
@@ -46,6 +46,11 @@ static void set_function_pathlist(Query *root, RelOptInfo *rel,
46
46
RangeTblEntry * rte );
47
47
static RelOptInfo * make_one_rel_by_joins (Query * root , int levels_needed ,
48
48
List * initial_rels );
49
+ static bool subquery_is_pushdown_safe (Query * subquery , Query * topquery );
50
+ static bool recurse_pushdown_safe (Node * setOp , Query * topquery );
51
+ static void subquery_push_qual (Query * subquery , Index rti , Node * qual );
52
+ static void recurse_push_qual (Node * setOp , Query * topquery ,
53
+ Index rti , Node * qual );
49
54
50
55
51
56
/*
@@ -297,43 +302,20 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
297
302
* generate a better plan for the subquery than evaluating all the
298
303
* subquery output rows and then filtering them.
299
304
*
300
- * There are several cases where we cannot push down clauses:
301
- *
302
- * 1. If the subquery contains set ops (UNION/INTERSECT/EXCEPT) we do not
303
- * push down any qual clauses, since the planner doesn't support quals
304
- * at the top level of a setop. (With suitable analysis we could try
305
- * to push the quals down into the component queries of the setop, but
306
- * getting it right seems nontrivial. Work on this later.)
307
- *
308
- * 2. If the subquery has a LIMIT clause or a DISTINCT ON clause, we must
309
- * not push down any quals, since that could change the set of rows
310
- * returned. (Actually, we could push down quals into a DISTINCT ON
311
- * subquery if they refer only to DISTINCT-ed output columns, but
312
- * checking that seems more work than it's worth. In any case, a
313
- * plain DISTINCT is safe to push down past.)
314
- *
315
- * 3. If the subquery has any functions returning sets in its target list,
316
- * we do not push down any quals, since the quals
317
- * might refer to those tlist items, which would mean we'd introduce
318
- * functions-returning-sets into the subquery's WHERE/HAVING quals.
319
- * (It'd be sufficient to not push down quals that refer to those
320
- * particular tlist items, but that's much clumsier to check.)
321
- *
322
- * 4. We do not push down clauses that contain subselects, mainly because
323
- * I'm not sure it will work correctly (the subplan hasn't yet
324
- * transformed sublinks to subselects).
305
+ * There are several cases where we cannot push down clauses.
306
+ * Restrictions involving the subquery are checked by
307
+ * subquery_is_pushdown_safe(). Also, we do not push down clauses that
308
+ * contain subselects, mainly because I'm not sure it will work correctly
309
+ * (the subplan hasn't yet transformed sublinks to subselects).
325
310
*
326
311
* Non-pushed-down clauses will get evaluated as qpquals of the
327
312
* SubqueryScan node.
328
313
*
329
314
* XXX Are there any cases where we want to make a policy decision not to
330
315
* push down, because it'd result in a worse plan?
331
316
*/
332
- if (subquery -> setOperations == NULL &&
333
- subquery -> limitOffset == NULL &&
334
- subquery -> limitCount == NULL &&
335
- !has_distinct_on_clause (subquery ) &&
336
- !expression_returns_set ((Node * ) subquery -> targetList ))
317
+ if (rel -> baserestrictinfo != NIL &&
318
+ subquery_is_pushdown_safe (subquery , subquery ))
337
319
{
338
320
/* OK to consider pushing down individual quals */
339
321
List * upperrestrictlist = NIL ;
@@ -351,25 +333,8 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
351
333
}
352
334
else
353
335
{
354
- /*
355
- * We need to replace Vars in the clause (which must refer
356
- * to outputs of the subquery) with copies of the
357
- * subquery's targetlist expressions. Note that at this
358
- * point, any uplevel Vars in the clause should have been
359
- * replaced with Params, so they need no work.
360
- */
361
- clause = ResolveNew (clause , rti , 0 ,
362
- subquery -> targetList ,
363
- CMD_SELECT , 0 );
364
- subquery -> havingQual = make_and_qual (subquery -> havingQual ,
365
- clause );
366
-
367
- /*
368
- * We need not change the subquery's hasAggs or
369
- * hasSublinks flags, since we can't be pushing down any
370
- * aggregates that weren't there before, and we don't push
371
- * down subselects at all.
372
- */
336
+ /* Push it down */
337
+ subquery_push_qual (subquery , rti , clause );
373
338
}
374
339
}
375
340
rel -> baserestrictinfo = upperrestrictlist ;
@@ -547,7 +512,183 @@ make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
547
512
}
548
513
549
514
/*****************************************************************************
515
+ * PUSHING QUALS DOWN INTO SUBQUERIES
516
+ *****************************************************************************/
517
+
518
+ /*
519
+ * subquery_is_pushdown_safe - is a subquery safe for pushing down quals?
520
+ *
521
+ * subquery is the particular component query being checked. topquery
522
+ * is the top component of a set-operations tree (the same Query if no
523
+ * set-op is involved).
524
+ *
525
+ * Conditions checked here:
550
526
*
527
+ * 1. If the subquery has a LIMIT clause or a DISTINCT ON clause, we must
528
+ * not push down any quals, since that could change the set of rows
529
+ * returned. (Actually, we could push down quals into a DISTINCT ON
530
+ * subquery if they refer only to DISTINCT-ed output columns, but
531
+ * checking that seems more work than it's worth. In any case, a
532
+ * plain DISTINCT is safe to push down past.)
533
+ *
534
+ * 2. If the subquery has any functions returning sets in its target list,
535
+ * we do not push down any quals, since the quals
536
+ * might refer to those tlist items, which would mean we'd introduce
537
+ * functions-returning-sets into the subquery's WHERE/HAVING quals.
538
+ * (It'd be sufficient to not push down quals that refer to those
539
+ * particular tlist items, but that's much clumsier to check.)
540
+ *
541
+ * 3. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
542
+ * quals into it, because that would change the results. For subqueries
543
+ * using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can push the quals
544
+ * into each component query, so long as all the component queries share
545
+ * identical output types. (That restriction could probably be relaxed,
546
+ * but it would take much more code to include type coercion code into
547
+ * the quals, and I'm also concerned about possible semantic gotchas.)
548
+ */
549
+ static bool
550
+ subquery_is_pushdown_safe (Query * subquery , Query * topquery )
551
+ {
552
+ SetOperationStmt * topop ;
553
+
554
+ /* Check points 1 and 2 */
555
+ if (subquery -> limitOffset != NULL ||
556
+ subquery -> limitCount != NULL ||
557
+ has_distinct_on_clause (subquery ) ||
558
+ expression_returns_set ((Node * ) subquery -> targetList ))
559
+ return false;
560
+
561
+ /* Are we at top level, or looking at a setop component? */
562
+ if (subquery == topquery )
563
+ {
564
+ /* Top level, so check any component queries */
565
+ if (subquery -> setOperations != NULL )
566
+ if (!recurse_pushdown_safe (subquery -> setOperations , topquery ))
567
+ return false;
568
+ }
569
+ else
570
+ {
571
+ /* Setop component must not have more components (too weird) */
572
+ if (subquery -> setOperations != NULL )
573
+ return false;
574
+ /* Setop component output types must match top level */
575
+ topop = (SetOperationStmt * ) topquery -> setOperations ;
576
+ Assert (topop && IsA (topop , SetOperationStmt ));
577
+ if (!tlist_same_datatypes (subquery -> targetList ,
578
+ topop -> colTypes ,
579
+ true))
580
+ return false;
581
+
582
+ }
583
+ return true;
584
+ }
585
+
586
+ /*
587
+ * Helper routine to recurse through setOperations tree
588
+ */
589
+ static bool
590
+ recurse_pushdown_safe (Node * setOp , Query * topquery )
591
+ {
592
+ if (IsA (setOp , RangeTblRef ))
593
+ {
594
+ RangeTblRef * rtr = (RangeTblRef * ) setOp ;
595
+ RangeTblEntry * rte = rt_fetch (rtr -> rtindex , topquery -> rtable );
596
+ Query * subquery = rte -> subquery ;
597
+
598
+ Assert (subquery != NULL );
599
+ return subquery_is_pushdown_safe (subquery , topquery );
600
+ }
601
+ else if (IsA (setOp , SetOperationStmt ))
602
+ {
603
+ SetOperationStmt * op = (SetOperationStmt * ) setOp ;
604
+
605
+ /* EXCEPT is no good */
606
+ if (op -> op == SETOP_EXCEPT )
607
+ return false;
608
+ /* Else recurse */
609
+ if (!recurse_pushdown_safe (op -> larg , topquery ))
610
+ return false;
611
+ if (!recurse_pushdown_safe (op -> rarg , topquery ))
612
+ return false;
613
+ }
614
+ else
615
+ {
616
+ elog (ERROR , "recurse_pushdown_safe: unexpected node %d" ,
617
+ (int ) nodeTag (setOp ));
618
+ }
619
+ return true;
620
+ }
621
+
622
+ /*
623
+ * subquery_push_qual - push down a qual that we have determined is safe
624
+ */
625
+ static void
626
+ subquery_push_qual (Query * subquery , Index rti , Node * qual )
627
+ {
628
+ if (subquery -> setOperations != NULL )
629
+ {
630
+ /* Recurse to push it separately to each component query */
631
+ recurse_push_qual (subquery -> setOperations , subquery , rti , qual );
632
+ }
633
+ else
634
+ {
635
+ /*
636
+ * We need to replace Vars in the qual (which must refer
637
+ * to outputs of the subquery) with copies of the
638
+ * subquery's targetlist expressions. Note that at this
639
+ * point, any uplevel Vars in the qual should have been
640
+ * replaced with Params, so they need no work.
641
+ *
642
+ * This step also ensures that when we are pushing into a setop
643
+ * tree, each component query gets its own copy of the qual.
644
+ */
645
+ qual = ResolveNew (qual , rti , 0 ,
646
+ subquery -> targetList ,
647
+ CMD_SELECT , 0 );
648
+ subquery -> havingQual = make_and_qual (subquery -> havingQual ,
649
+ qual );
650
+
651
+ /*
652
+ * We need not change the subquery's hasAggs or
653
+ * hasSublinks flags, since we can't be pushing down any
654
+ * aggregates that weren't there before, and we don't push
655
+ * down subselects at all.
656
+ */
657
+ }
658
+ }
659
+
660
+ /*
661
+ * Helper routine to recurse through setOperations tree
662
+ */
663
+ static void
664
+ recurse_push_qual (Node * setOp , Query * topquery ,
665
+ Index rti , Node * qual )
666
+ {
667
+ if (IsA (setOp , RangeTblRef ))
668
+ {
669
+ RangeTblRef * rtr = (RangeTblRef * ) setOp ;
670
+ RangeTblEntry * rte = rt_fetch (rtr -> rtindex , topquery -> rtable );
671
+ Query * subquery = rte -> subquery ;
672
+
673
+ Assert (subquery != NULL );
674
+ subquery_push_qual (subquery , rti , qual );
675
+ }
676
+ else if (IsA (setOp , SetOperationStmt ))
677
+ {
678
+ SetOperationStmt * op = (SetOperationStmt * ) setOp ;
679
+
680
+ recurse_push_qual (op -> larg , topquery , rti , qual );
681
+ recurse_push_qual (op -> rarg , topquery , rti , qual );
682
+ }
683
+ else
684
+ {
685
+ elog (ERROR , "recurse_push_qual: unexpected node %d" ,
686
+ (int ) nodeTag (setOp ));
687
+ }
688
+ }
689
+
690
+ /*****************************************************************************
691
+ * DEBUG SUPPORT
551
692
*****************************************************************************/
552
693
553
694
#ifdef OPTIMIZER_DEBUG
0 commit comments