diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 24f0ebf54dc605ac2e996121a1fb32c312d53613..e6791dddd69f9cbdfe558df126a579cf240a099e 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.101 2003/03/22 17:11:25 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.102 2003/04/24 23:43:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,9 +49,14 @@ static void set_function_pathlist(Query *root, RelOptInfo *rel,
 					  RangeTblEntry *rte);
 static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
 					  List *initial_rels);
-static bool subquery_is_pushdown_safe(Query *subquery, Query *topquery);
-static bool recurse_pushdown_safe(Node *setOp, Query *topquery);
-static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual);
+static bool subquery_is_pushdown_safe(Query *subquery, Query *topquery,
+									  bool *differentTypes);
+static bool recurse_pushdown_safe(Node *setOp, Query *topquery,
+								  bool *differentTypes);
+static void compare_tlist_datatypes(List *tlist, List *colTypes,
+									bool *differentTypes);
+static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
+								  bool *differentTypes);
 static void subquery_push_qual(Query *subquery, Index rti, Node *qual);
 static void recurse_push_qual(Node *setOp, Query *topquery,
 				  Index rti, Node *qual);
@@ -294,8 +299,13 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
 					  Index rti, RangeTblEntry *rte)
 {
 	Query	   *subquery = rte->subquery;
+	bool	   *differentTypes;
 	List	   *pathkeys;
 
+	/* We need a workspace for keeping track of set-op type coercions */
+	differentTypes = (bool *)
+		palloc0((length(subquery->targetList) + 1) * sizeof(bool));
+
 	/*
 	 * If there are any restriction clauses that have been attached to the
 	 * subquery relation, consider pushing them down to become HAVING
@@ -318,7 +328,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
 	 * push down a pushable qual, because it'd result in a worse plan?
 	 */
 	if (rel->baserestrictinfo != NIL &&
-		subquery_is_pushdown_safe(subquery, subquery))
+		subquery_is_pushdown_safe(subquery, subquery, differentTypes))
 	{
 		/* OK to consider pushing down individual quals */
 		List	   *upperrestrictlist = NIL;
@@ -329,7 +339,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
 			RestrictInfo *rinfo = (RestrictInfo *) lfirst(lst);
 			Node	   *clause = (Node *) rinfo->clause;
 
-			if (qual_is_pushdown_safe(subquery, rti, clause))
+			if (qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
 			{
 				/* Push it down */
 				subquery_push_qual(subquery, rti, clause);
@@ -343,6 +353,8 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
 		rel->baserestrictinfo = upperrestrictlist;
 	}
 
+	pfree(differentTypes);
+
 	/* Generate the plan for the subquery */
 	rel->subplan = subquery_planner(subquery, 0.0 /* default case */ );
 
@@ -532,15 +544,19 @@ make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
  * since that could change the set of rows returned.
  *
  * 2. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
- * quals into it, because that would change the results.  For subqueries
- * using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can push the quals
- * into each component query, so long as all the component queries share
- * identical output types.	(That restriction could probably be relaxed,
- * but it would take much more code to include type coercion code into
- * the quals, and I'm also concerned about possible semantic gotchas.)
+ * quals into it, because that would change the results.
+ *
+ * 3. For subqueries using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can
+ * push quals into each component query, but the quals can only reference
+ * subquery columns that suffer no type coercions in the set operation.
+ * Otherwise there are possible semantic gotchas.  So, we check the
+ * component queries to see if any of them have different output types;
+ * differentTypes[k] is set true if column k has different type in any
+ * component.
  */
 static bool
-subquery_is_pushdown_safe(Query *subquery, Query *topquery)
+subquery_is_pushdown_safe(Query *subquery, Query *topquery,
+						  bool *differentTypes)
 {
 	SetOperationStmt *topop;
 
@@ -553,7 +569,8 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery)
 	{
 		/* Top level, so check any component queries */
 		if (subquery->setOperations != NULL)
-			if (!recurse_pushdown_safe(subquery->setOperations, topquery))
+			if (!recurse_pushdown_safe(subquery->setOperations, topquery,
+									   differentTypes))
 				return false;
 	}
 	else
@@ -561,14 +578,12 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery)
 		/* Setop component must not have more components (too weird) */
 		if (subquery->setOperations != NULL)
 			return false;
-		/* Setop component output types must match top level */
+		/* Check whether setop component output types match top level */
 		topop = (SetOperationStmt *) topquery->setOperations;
 		Assert(topop && IsA(topop, SetOperationStmt));
-		if (!tlist_same_datatypes(subquery->targetList,
-								  topop->colTypes,
-								  true))
-			return false;
-
+		compare_tlist_datatypes(subquery->targetList,
+								topop->colTypes,
+								differentTypes);
 	}
 	return true;
 }
@@ -577,7 +592,8 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery)
  * Helper routine to recurse through setOperations tree
  */
 static bool
-recurse_pushdown_safe(Node *setOp, Query *topquery)
+recurse_pushdown_safe(Node *setOp, Query *topquery,
+					  bool *differentTypes)
 {
 	if (IsA(setOp, RangeTblRef))
 	{
@@ -586,7 +602,7 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
 		Query	   *subquery = rte->subquery;
 
 		Assert(subquery != NULL);
-		return subquery_is_pushdown_safe(subquery, topquery);
+		return subquery_is_pushdown_safe(subquery, topquery, differentTypes);
 	}
 	else if (IsA(setOp, SetOperationStmt))
 	{
@@ -596,9 +612,9 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
 		if (op->op == SETOP_EXCEPT)
 			return false;
 		/* Else recurse */
-		if (!recurse_pushdown_safe(op->larg, topquery))
+		if (!recurse_pushdown_safe(op->larg, topquery, differentTypes))
 			return false;
-		if (!recurse_pushdown_safe(op->rarg, topquery))
+		if (!recurse_pushdown_safe(op->rarg, topquery, differentTypes))
 			return false;
 	}
 	else
@@ -609,6 +625,33 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
 	return true;
 }
 
+/*
+ * Compare tlist's datatypes against the list of set-operation result types.
+ * For any items that are different, mark the appropriate element of
+ * differentTypes[] to show that this column will have type conversions.
+ */
+static void
+compare_tlist_datatypes(List *tlist, List *colTypes,
+						bool *differentTypes)
+{
+	List	   *i;
+
+	foreach(i, tlist)
+	{
+		TargetEntry *tle = (TargetEntry *) lfirst(i);
+
+		if (tle->resdom->resjunk)
+			continue;			/* ignore resjunk columns */
+		if (colTypes == NIL)
+			elog(ERROR, "wrong number of tlist entries");
+		if (tle->resdom->restype != lfirsto(colTypes))
+			differentTypes[tle->resdom->resno] = true;
+		colTypes = lnext(colTypes);
+	}
+	if (colTypes != NIL)
+		elog(ERROR, "wrong number of tlist entries");
+}
+
 /*
  * qual_is_pushdown_safe - is a particular qual safe to push down?
  *
@@ -621,7 +664,11 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
  * it will work correctly: sublinks will already have been transformed into
  * subplans in the qual, but not in the subquery).
  *
- * 2. If the subquery uses DISTINCT ON, we must not push down any quals that
+ * 2. The qual must not refer to any subquery output columns that were
+ * found to have inconsistent types across a set operation tree by
+ * subquery_is_pushdown_safe().
+ *
+ * 3. If the subquery uses DISTINCT ON, we must not push down any quals that
  * refer to non-DISTINCT output columns, because that could change the set
  * of rows returned.  This condition is vacuous for DISTINCT, because then
  * there are no non-DISTINCT output columns, but unfortunately it's fairly
@@ -629,12 +676,13 @@ recurse_pushdown_safe(Node *setOp, Query *topquery)
  * parsetree representation.  It's cheaper to just make sure all the Vars
  * in the qual refer to DISTINCT columns.
  *
- * 3. We must not push down any quals that refer to subselect outputs that
+ * 4. We must not push down any quals that refer to subselect outputs that
  * return sets, else we'd introduce functions-returning-sets into the
  * subquery's WHERE/HAVING quals.
  */
 static bool
-qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual)
+qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
+					  bool *differentTypes)
 {
 	bool		safe = true;
 	List	   *vars;
@@ -666,6 +714,14 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual)
 			continue;
 		tested = bms_add_member(tested, var->varattno);
 
+		/* Check point 2 */
+		if (differentTypes[var->varattno])
+		{
+			safe = false;
+			break;
+		}
+
+		/* Must find the tlist element referenced by the Var */
 		foreach(tl, subquery->targetList)
 		{
 			tle = (TargetEntry *) lfirst(tl);
@@ -675,7 +731,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual)
 		Assert(tl != NIL);
 		Assert(!tle->resdom->resjunk);
 
-		/* If subquery uses DISTINCT or DISTINCT ON, check point 2 */
+		/* If subquery uses DISTINCT or DISTINCT ON, check point 3 */
 		if (subquery->distinctClause != NIL &&
 			!targetIsInSortList(tle, subquery->distinctClause))
 		{
@@ -684,7 +740,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual)
 			break;
 		}
 
-		/* Refuse functions returning sets (point 3) */
+		/* Refuse functions returning sets (point 4) */
 		if (expression_returns_set((Node *) tle->expr))
 		{
 			safe = false;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 1782bcc88b953a1c5e34258bb8f13302aeb62349..fb7bf9e70700ad084a93eb474bb524271d290095 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.92 2003/03/10 03:53:50 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.93 2003/04/24 23:43:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -60,6 +60,7 @@ static List *generate_setop_tlist(List *colTypes, int flag,
 static List *generate_append_tlist(List *colTypes, bool flag,
 					  List *input_plans,
 					  List *refnames_tlist);
+static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
 static Node *adjust_inherited_attrs_mutator(Node *node,
 							   adjust_inherited_attrs_context *context);
 static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
@@ -572,7 +573,7 @@ generate_append_tlist(List *colTypes, bool flag,
  * Resjunk columns are ignored if junkOK is true; otherwise presence of
  * a resjunk column will always cause a 'false' result.
  */
-bool
+static bool
 tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
 {
 	List	   *i;
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index 5cde6096a1e9d9454c64038b3f8e2cc612db6413..52d045c51a4d569ffcefe09bbd6482422f4957bb 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: prep.h,v 1.38 2003/03/05 20:01:04 tgl Exp $
+ * $Id: prep.h,v 1.39 2003/04/24 23:43:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,6 +59,4 @@ extern Node *adjust_inherited_attrs(Node *node,
 					   Index old_rt_index, Oid old_relid,
 					   Index new_rt_index, Oid new_relid);
 
-extern bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
-
 #endif   /* PREP_H */