88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.101 2003/03/22 17:11:25 tgl Exp $
11+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.102 2003/04/24 23:43:09 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -49,9 +49,14 @@ static void set_function_pathlist(Query *root, RelOptInfo *rel,
4949RangeTblEntry * rte );
5050static RelOptInfo * make_one_rel_by_joins (Query * root ,int levels_needed ,
5151List * initial_rels );
52- static bool subquery_is_pushdown_safe (Query * subquery ,Query * topquery );
53- static bool recurse_pushdown_safe (Node * setOp ,Query * topquery );
54- static bool qual_is_pushdown_safe (Query * subquery ,Index rti ,Node * qual );
52+ static bool subquery_is_pushdown_safe (Query * subquery ,Query * topquery ,
53+ bool * differentTypes );
54+ static bool recurse_pushdown_safe (Node * setOp ,Query * topquery ,
55+ bool * differentTypes );
56+ static void compare_tlist_datatypes (List * tlist ,List * colTypes ,
57+ bool * differentTypes );
58+ static bool qual_is_pushdown_safe (Query * subquery ,Index rti ,Node * qual ,
59+ bool * differentTypes );
5560static void subquery_push_qual (Query * subquery ,Index rti ,Node * qual );
5661static void recurse_push_qual (Node * setOp ,Query * topquery ,
5762Index rti ,Node * qual );
@@ -294,8 +299,13 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
294299Index rti ,RangeTblEntry * rte )
295300{
296301Query * subquery = rte -> subquery ;
302+ bool * differentTypes ;
297303List * pathkeys ;
298304
305+ /* We need a workspace for keeping track of set-op type coercions */
306+ differentTypes = (bool * )
307+ palloc0 ((length (subquery -> targetList )+ 1 )* sizeof (bool ));
308+
299309/*
300310 * If there are any restriction clauses that have been attached to the
301311 * subquery relation, consider pushing them down to become HAVING
@@ -318,7 +328,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
318328 * push down a pushable qual, because it'd result in a worse plan?
319329 */
320330if (rel -> baserestrictinfo != NIL &&
321- subquery_is_pushdown_safe (subquery ,subquery ))
331+ subquery_is_pushdown_safe (subquery ,subquery , differentTypes ))
322332{
323333/* OK to consider pushing down individual quals */
324334List * upperrestrictlist = NIL ;
@@ -329,7 +339,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
329339RestrictInfo * rinfo = (RestrictInfo * )lfirst (lst );
330340Node * clause = (Node * )rinfo -> clause ;
331341
332- if (qual_is_pushdown_safe (subquery ,rti ,clause ))
342+ if (qual_is_pushdown_safe (subquery ,rti ,clause , differentTypes ))
333343{
334344/* Push it down */
335345subquery_push_qual (subquery ,rti ,clause );
@@ -343,6 +353,8 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
343353rel -> baserestrictinfo = upperrestrictlist ;
344354}
345355
356+ pfree (differentTypes );
357+
346358/* Generate the plan for the subquery */
347359rel -> subplan = subquery_planner (subquery ,0.0 /* default case */ );
348360
@@ -532,15 +544,19 @@ make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
532544 * since that could change the set of rows returned.
533545 *
534546 * 2. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
535- * quals into it, because that would change the results. For subqueries
536- * using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can push the quals
537- * into each component query, so long as all the component queries share
538- * identical output types.(That restriction could probably be relaxed,
539- * but it would take much more code to include type coercion code into
540- * the quals, and I'm also concerned about possible semantic gotchas.)
547+ * quals into it, because that would change the results.
548+ *
549+ * 3. For subqueries using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can
550+ * push quals into each component query, but the quals can only reference
551+ * subquery columns that suffer no type coercions in the set operation.
552+ * Otherwise there are possible semantic gotchas. So, we check the
553+ * component queries to see if any of them have different output types;
554+ * differentTypes[k] is set true if column k has different type in any
555+ * component.
541556 */
542557static bool
543- subquery_is_pushdown_safe (Query * subquery ,Query * topquery )
558+ subquery_is_pushdown_safe (Query * subquery ,Query * topquery ,
559+ bool * differentTypes )
544560{
545561SetOperationStmt * topop ;
546562
@@ -553,22 +569,21 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery)
553569{
554570/* Top level, so check any component queries */
555571if (subquery -> setOperations != NULL )
556- if (!recurse_pushdown_safe (subquery -> setOperations ,topquery ))
572+ if (!recurse_pushdown_safe (subquery -> setOperations ,topquery ,
573+ differentTypes ))
557574return false;
558575}
559576else
560577{
561578/* Setop component must not have more components (too weird) */
562579if (subquery -> setOperations != NULL )
563580return false;
564- /*Setop component output types must match top level */
581+ /*Check whether setop component output types match top level */
565582topop = (SetOperationStmt * )topquery -> setOperations ;
566583Assert (topop && IsA (topop ,SetOperationStmt ));
567- if (!tlist_same_datatypes (subquery -> targetList ,
568- topop -> colTypes ,
569- true))
570- return false;
571-
584+ compare_tlist_datatypes (subquery -> targetList ,
585+ topop -> colTypes ,
586+ differentTypes );
572587}
573588return true;
574589}
@@ -577,7 +592,8 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery)
577592 * Helper routine to recurse through setOperations tree
578593 */
579594static bool
580- recurse_pushdown_safe (Node * setOp ,Query * topquery )
595+ recurse_pushdown_safe (Node * setOp ,Query * topquery ,
596+ bool * differentTypes )
581597{
582598if (IsA (setOp ,RangeTblRef ))
583599{
@@ -586,7 +602,7 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
586602Query * subquery = rte -> subquery ;
587603
588604Assert (subquery != NULL );
589- return subquery_is_pushdown_safe (subquery ,topquery );
605+ return subquery_is_pushdown_safe (subquery ,topquery , differentTypes );
590606}
591607else if (IsA (setOp ,SetOperationStmt ))
592608{
@@ -596,9 +612,9 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
596612if (op -> op == SETOP_EXCEPT )
597613return false;
598614/* Else recurse */
599- if (!recurse_pushdown_safe (op -> larg ,topquery ))
615+ if (!recurse_pushdown_safe (op -> larg ,topquery , differentTypes ))
600616return false;
601- if (!recurse_pushdown_safe (op -> rarg ,topquery ))
617+ if (!recurse_pushdown_safe (op -> rarg ,topquery , differentTypes ))
602618return false;
603619}
604620else
@@ -609,6 +625,33 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
609625return true;
610626}
611627
628+ /*
629+ * Compare tlist's datatypes against the list of set-operation result types.
630+ * For any items that are different, mark the appropriate element of
631+ * differentTypes[] to show that this column will have type conversions.
632+ */
633+ static void
634+ compare_tlist_datatypes (List * tlist ,List * colTypes ,
635+ bool * differentTypes )
636+ {
637+ List * i ;
638+
639+ foreach (i ,tlist )
640+ {
641+ TargetEntry * tle = (TargetEntry * )lfirst (i );
642+
643+ if (tle -> resdom -> resjunk )
644+ continue ;/* ignore resjunk columns */
645+ if (colTypes == NIL )
646+ elog (ERROR ,"wrong number of tlist entries" );
647+ if (tle -> resdom -> restype != lfirsto (colTypes ))
648+ differentTypes [tle -> resdom -> resno ]= true;
649+ colTypes = lnext (colTypes );
650+ }
651+ if (colTypes != NIL )
652+ elog (ERROR ,"wrong number of tlist entries" );
653+ }
654+
612655/*
613656 * qual_is_pushdown_safe - is a particular qual safe to push down?
614657 *
@@ -621,20 +664,25 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
621664 * it will work correctly: sublinks will already have been transformed into
622665 * subplans in the qual, but not in the subquery).
623666 *
624- * 2. If the subquery uses DISTINCT ON, we must not push down any quals that
667+ * 2. The qual must not refer to any subquery output columns that were
668+ * found to have inconsistent types across a set operation tree by
669+ * subquery_is_pushdown_safe().
670+ *
671+ * 3. If the subquery uses DISTINCT ON, we must not push down any quals that
625672 * refer to non-DISTINCT output columns, because that could change the set
626673 * of rows returned. This condition is vacuous for DISTINCT, because then
627674 * there are no non-DISTINCT output columns, but unfortunately it's fairly
628675 * expensive to tell the difference between DISTINCT and DISTINCT ON in the
629676 * parsetree representation. It's cheaper to just make sure all the Vars
630677 * in the qual refer to DISTINCT columns.
631678 *
632- *3 . We must not push down any quals that refer to subselect outputs that
679+ *4 . We must not push down any quals that refer to subselect outputs that
633680 * return sets, else we'd introduce functions-returning-sets into the
634681 * subquery's WHERE/HAVING quals.
635682 */
636683static bool
637- qual_is_pushdown_safe (Query * subquery ,Index rti ,Node * qual )
684+ qual_is_pushdown_safe (Query * subquery ,Index rti ,Node * qual ,
685+ bool * differentTypes )
638686{
639687bool safe = true;
640688List * vars ;
@@ -666,6 +714,14 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual)
666714continue ;
667715tested = bms_add_member (tested ,var -> varattno );
668716
717+ /* Check point 2 */
718+ if (differentTypes [var -> varattno ])
719+ {
720+ safe = false;
721+ break ;
722+ }
723+
724+ /* Must find the tlist element referenced by the Var */
669725foreach (tl ,subquery -> targetList )
670726{
671727tle = (TargetEntry * )lfirst (tl );
@@ -675,7 +731,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual)
675731Assert (tl != NIL );
676732Assert (!tle -> resdom -> resjunk );
677733
678- /* If subquery uses DISTINCT or DISTINCT ON, check point2 */
734+ /* If subquery uses DISTINCT or DISTINCT ON, check point3 */
679735if (subquery -> distinctClause != NIL &&
680736!targetIsInSortList (tle ,subquery -> distinctClause ))
681737{
@@ -684,7 +740,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual)
684740break ;
685741}
686742
687- /* Refuse functions returning sets (point3 ) */
743+ /* Refuse functions returning sets (point4 ) */
688744if (expression_returns_set ((Node * )tle -> expr ))
689745{
690746safe = false;