From 944671820fbc9e97be7bfdb05589f69428536632 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 5 Mar 2002 05:10:24 +0000
Subject: [PATCH] Previous patch to mark UNION outputs with common typmod (if
 any) breaks three-or-more-way UNIONs, as per example from Josh Berkus.  Cause
 is a fragile assumption that one tlist's entries will exactly match another.
 Restructure code to make that assumption a little less fragile.

---
 src/backend/optimizer/prep/prepunion.c | 180 ++++++++++++++++---------
 1 file changed, 118 insertions(+), 62 deletions(-)

diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 9b7ea633b68..64d8b78f066 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.70 2002/03/01 06:01:20 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.71 2002/03/05 05:10:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -63,7 +63,9 @@ static List *generate_setop_tlist(List *colTypes, int flag,
 					 bool hack_constants,
 					 List *input_tlist,
 					 List *refnames_tlist);
-static void merge_tlist_typmods(List *tlist, List *planlist);
+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);
@@ -169,13 +171,11 @@ recurse_set_operations(Node *setOp, Query *parse,
 		 *
 		 * XXX you don't really want to know about this: setrefs.c will apply
 		 * replace_vars_with_subplan_refs() to the Result node's tlist.
-		 * This would fail if the input plan's non-resjunk tlist entries
-		 * were not all simple Vars equal() to the referencing Vars
-		 * generated by generate_setop_tlist().  However, since the input
-		 * plan was generated by generate_union_plan() or
-		 * generate_nonunion_plan(), the referencing Vars will equal the
-		 * tlist entries they reference. Ugly but I don't feel like making
-		 * that code more general right now.
+		 * This would fail if the Vars generated by generate_setop_tlist()
+		 * were not exactly equal() to the corresponding tlist entries of
+		 * the subplan.  However, since the subplan was generated by
+		 * generate_union_plan() or generate_nonunion_plan(), and hence its
+		 * tlist was generated by generate_append_tlist(), this will work.
 		 */
 		if (flag >= 0 ||
 			!tlist_same_datatypes(plan->targetlist, colTypes, junkOK))
@@ -226,10 +226,8 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
 	 * concerned, but we must make it look real anyway for the benefit of
 	 * the next plan level up.
 	 */
-	tlist = generate_setop_tlist(op->colTypes, -1, false,
-								 ((Plan *) lfirst(planlist))->targetlist,
-								 refnames_tlist);
-	merge_tlist_typmods(tlist, planlist);
+	tlist = generate_append_tlist(op->colTypes, false,
+								  planlist, refnames_tlist);
 
 	/*
 	 * Append the child results together.
@@ -285,10 +283,8 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
 	 * flag column is shown as a variable not a constant, else setrefs.c
 	 * will get confused.
 	 */
-	tlist = generate_setop_tlist(op->colTypes, 2, false,
-								 lplan->targetlist,
-								 refnames_tlist);
-	merge_tlist_typmods(tlist, planlist);
+	tlist = generate_append_tlist(op->colTypes, true,
+								  planlist, refnames_tlist);
 
 	/*
 	 * Append the child results together.
@@ -368,8 +364,7 @@ recurse_union_children(Node *setOp, Query *parse,
  * Generate targetlist for a set-operation plan node
  *
  * colTypes: column datatypes for non-junk columns
- * flag: -1 if no flag column needed, 0 or 1 to create a const flag column,
- *		 2 to create a variable flag column
+ * flag: -1 if no flag column needed, 0 or 1 to create a const flag column
  * hack_constants: true to copy up constants (see comments in code)
  * input_tlist: targetlist of this node's input node
  * refnames_tlist: targetlist to take column names from
@@ -450,26 +445,14 @@ generate_setop_tlist(List *colTypes, int flag,
 							-1,
 							pstrdup("flag"),
 							true);
-		if (flag <= 1)
-		{
-			/* flag value is the given constant */
-			expr = (Node *) makeConst(INT4OID,
-									  sizeof(int4),
-									  Int32GetDatum(flag),
-									  false,
-									  true,
-									  false,
-									  false);
-		}
-		else
-		{
-			/* flag value is being copied up from subplan */
-			expr = (Node *) makeVar(0,
-									resdom->resno,
-									INT4OID,
-									-1,
-									0);
-		}
+		/* flag value is the given constant */
+		expr = (Node *) makeConst(INT4OID,
+								  sizeof(int4),
+								  Int32GetDatum(flag),
+								  false,
+								  true,
+								  false,
+								  false);
 		tlist = lappend(tlist, makeTargetEntry(resdom, expr));
 	}
 
@@ -477,44 +460,117 @@ generate_setop_tlist(List *colTypes, int flag,
 }
 
 /*
- * Merge typmods of a list of set-operation subplans.
+ * Generate targetlist for a set-operation Append node
+ *
+ * colTypes: column datatypes for non-junk columns
+ * flag: true to create a flag column copied up from subplans
+ * input_plans: list of sub-plans of the Append
+ * refnames_tlist: targetlist to take column names from
  *
- * If the inputs all agree on type and typmod of a particular column,
- * use that typmod; else use -1.  We assume the result tlist has been
- * initialized with the types and typmods of the first input subplan.
+ * The entries in the Append's targetlist should always be simple Vars;
+ * we just have to make sure they have the right datatypes and typmods.
  */
-static void
-merge_tlist_typmods(List *tlist, List *planlist)
+static List *
+generate_append_tlist(List *colTypes, bool flag,
+					  List *input_plans,
+					  List *refnames_tlist)
 {
+	List	   *tlist = NIL;
+	int			resno = 1;
+	List	   *curColType;
+	int			colindex;
+	Resdom	   *resdom;
+	Node	   *expr;
 	List	   *planl;
+	int32	   *colTypmods;
+
+	/*
+	 * First extract typmods to use.
+	 *
+	 * If the inputs all agree on type and typmod of a particular column,
+	 * use that typmod; else use -1.
+	 */
+	colTypmods = (int32 *) palloc(length(colTypes) * sizeof(int32));
 
-	foreach(planl, planlist)
+	foreach(planl, input_plans)
 	{
 		Plan   *subplan = (Plan *) lfirst(planl);
-		List   *subtlist = subplan->targetlist;
-		List   *restlist;
+		List   *subtlist;
 
-		foreach(restlist, tlist)
+		curColType = colTypes;
+		colindex = 0;
+		foreach(subtlist, subplan->targetlist)
 		{
-			TargetEntry *restle = (TargetEntry *) lfirst(restlist);
-			TargetEntry *subtle;
+			TargetEntry *subtle = (TargetEntry *) lfirst(subtlist);
 
-			if (restle->resdom->resjunk)
+			if (subtle->resdom->resjunk)
 				continue;
-			Assert(subtlist != NIL);
-			subtle = (TargetEntry *) lfirst(subtlist);
-			while (subtle->resdom->resjunk)
+			Assert(curColType != NIL);
+			if (subtle->resdom->restype == (Oid) lfirsti(curColType))
 			{
-				subtlist = lnext(subtlist);
-				Assert(subtlist != NIL);
-				subtle = (TargetEntry *) lfirst(subtlist);
+				/* If first subplan, copy the typmod; else compare */
+				if (planl == input_plans)
+					colTypmods[colindex] = subtle->resdom->restypmod;
+				else if (subtle->resdom->restypmod != colTypmods[colindex])
+					colTypmods[colindex] = -1;
 			}
-			if (restle->resdom->restype != subtle->resdom->restype ||
-				restle->resdom->restypmod != subtle->resdom->restypmod)
-				restle->resdom->restypmod = -1;
-			subtlist = lnext(subtlist);
+			else
+			{
+				/* types disagree, so force typmod to -1 */
+				colTypmods[colindex] = -1;
+			}
+			curColType = lnext(curColType);
+			colindex++;
 		}
+		Assert(curColType == NIL);
 	}
+
+	/*
+	 * Now we can build the tlist for the Append.
+	 */
+	colindex = 0;
+	foreach(curColType, colTypes)
+	{
+		Oid			colType = (Oid) lfirsti(curColType);
+		int32		colTypmod = colTypmods[colindex++];
+		TargetEntry *reftle = (TargetEntry *) lfirst(refnames_tlist);
+
+		Assert(reftle->resdom->resno == resno);
+		Assert(!reftle->resdom->resjunk);
+		expr = (Node *) makeVar(0,
+								resno,
+								colType,
+								colTypmod,
+								0);
+		resdom = makeResdom((AttrNumber) resno++,
+							colType,
+							colTypmod,
+							pstrdup(reftle->resdom->resname),
+							false);
+		tlist = lappend(tlist, makeTargetEntry(resdom, expr));
+		refnames_tlist = lnext(refnames_tlist);
+	}
+
+	if (flag)
+	{
+		/* Add a resjunk flag column */
+		resdom = makeResdom((AttrNumber) resno++,
+							INT4OID,
+							-1,
+							pstrdup("flag"),
+							true);
+		/* flag value is shown as copied up from subplan */
+		expr = (Node *) makeVar(0,
+								resdom->resno,
+								INT4OID,
+								-1,
+								0);
+		tlist = lappend(tlist, makeTargetEntry(resdom, expr));
+	}
+
+	pfree(colTypmods);
+
+	return tlist;
 }
 
 /*
-- 
GitLab