diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 5798b726497da09cf634eb3eb9b38df142130218..45c6ead0fc04caa03454bdf18e766fe253b8f6b3 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -14,7 +14,7 @@
  * Copyright (c) 2003, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/bitmapset.c,v 1.1 2003/02/08 20:20:53 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/bitmapset.c,v 1.2 2003/06/29 23:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -388,6 +388,36 @@ bms_overlap(const Bitmapset *a, const Bitmapset *b)
 	return false;
 }
 
+/*
+ * bms_nonempty_difference - do sets have a nonempty difference?
+ */
+bool
+bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
+{
+	int			shortlen;
+	int			i;
+
+	/* Handle cases where either input is NULL */
+	if (a == NULL)
+		return false;
+	if (b == NULL)
+		return !bms_is_empty(a);
+	/* Check words in common */
+	shortlen = Min(a->nwords, b->nwords);
+	for (i = 0; i < shortlen; i++)
+	{
+		if ((a->words[i] & ~ b->words[i]) != 0)
+			return true;
+	}
+	/* Check extra words in a */
+	for (; i < a->nwords; i++)
+	{
+		if (a->words[i] != 0)
+			return true;
+	}
+	return false;
+}
+
 /*
  * bms_singleton_member - return the sole integer member of set
  *
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index e6791dddd69f9cbdfe558df126a579cf240a099e..cd04af9f821ee2feb35e6d939829cebda6fef630 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.102 2003/04/24 23:43:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.103 2003/06/29 23:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -235,6 +235,9 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
 		RangeTblEntry *childrte;
 		Oid			childOID;
 		RelOptInfo *childrel;
+		List	   *reltlist;
+		List	   *parentvars;
+		List	   *childvars;
 
 		childrte = rt_fetch(childRTindex, root->rtable);
 		childOID = childrte->relid;
@@ -251,21 +254,24 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
 		 * Copy the parent's targetlist and restriction quals to the
 		 * child, with attribute-number adjustment as needed.  We don't
 		 * bother to copy the join quals, since we can't do any joining of
-		 * the individual tables.
+		 * the individual tables.  Also, we just zap attr_needed rather
+		 * than trying to adjust it; it won't be looked at in the child.
 		 */
-		childrel->targetlist = (List *)
-			adjust_inherited_attrs((Node *) rel->targetlist,
+		reltlist = FastListValue(&rel->reltargetlist);
+		reltlist = (List *)
+			adjust_inherited_attrs((Node *) reltlist,
 								   parentRTindex,
 								   parentOID,
 								   childRTindex,
 								   childOID);
+		FastListFromList(&childrel->reltargetlist, reltlist);
+		childrel->attr_needed = NULL;
 		childrel->baserestrictinfo = (List *)
 			adjust_inherited_attrs((Node *) rel->baserestrictinfo,
 								   parentRTindex,
 								   parentOID,
 								   childRTindex,
 								   childOID);
-		childrel->baserestrictcost = rel->baserestrictcost;
 
 		/*
 		 * Now compute child access paths, and save the cheapest.
@@ -274,10 +280,27 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
 
 		subpaths = lappend(subpaths, childrel->cheapest_total_path);
 
-		/* Also update total size estimates */
+		/*
+		 * Propagate size information from the child back to the parent.
+		 * For simplicity, we use the largest widths from any child as the
+		 * parent estimates.
+		 */
 		rel->rows += childrel->rows;
 		if (childrel->width > rel->width)
 			rel->width = childrel->width;
+		
+		childvars = FastListValue(&childrel->reltargetlist);
+		foreach(parentvars, FastListValue(&rel->reltargetlist))
+		{
+			Var	   *parentvar = (Var *) lfirst(parentvars);
+			Var	   *childvar = (Var *) lfirst(childvars);
+			int		parentndx = parentvar->varattno - rel->min_attr;
+			int		childndx = childvar->varattno - childrel->min_attr;
+
+			if (childrel->attr_widths[childndx] > rel->attr_widths[parentndx])
+				rel->attr_widths[parentndx] = childrel->attr_widths[childndx];
+			childvars = lnext(childvars);
+		}
 	}
 
 	/*
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 283987b63d53275846f7896ed546d97f68297638..34a3aaf1c949b5a17303ee62bd208f3dececc14a 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -49,7 +49,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.108 2003/06/29 00:33:43 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.109 2003/06/29 23:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1792,13 +1792,9 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
 	rel->rows = temp;
 
 	/*
-	 * We could apply set_rel_width() to compute the output tuple width
-	 * from scratch, but at present it's always just the sum of the input
-	 * widths, so why work harder than necessary?  If relnode.c is ever
-	 * taught to remove unneeded columns from join targetlists, go back to
-	 * using set_rel_width here.
+	 * We need not compute the output width here, because build_joinrel_tlist
+	 * already did.
 	 */
-	rel->width = outer_rel->width + inner_rel->width;
 }
 
 /*
@@ -1858,13 +1854,16 @@ set_function_size_estimates(Query *root, RelOptInfo *rel)
 
 /*
  * set_rel_width
- *		Set the estimated output width of the relation.
+ *		Set the estimated output width of a base relation.
  *
- * NB: this works best on base relations because it prefers to look at
+ * NB: this works best on plain relations because it prefers to look at
  * real Vars.  It will fail to make use of pg_statistic info when applied
  * to a subquery relation, even if the subquery outputs are simple vars
  * that we could have gotten info for.	Is it worth trying to be smarter
  * about subqueries?
+ *
+ * The per-attribute width estimates are cached for possible re-use while
+ * building join relations.
  */
 static void
 set_rel_width(Query *root, RelOptInfo *rel)
@@ -1872,38 +1871,41 @@ set_rel_width(Query *root, RelOptInfo *rel)
 	int32		tuple_width = 0;
 	List	   *tllist;
 
-	foreach(tllist, rel->targetlist)
+	foreach(tllist, FastListValue(&rel->reltargetlist))
 	{
-		TargetEntry *tle = (TargetEntry *) lfirst(tllist);
+		Var		   *var = (Var *) lfirst(tllist);
+		int			ndx = var->varattno - rel->min_attr;
+		Oid			relid;
 		int32		item_width;
 
-		/*
-		 * If it's a Var, try to get statistical info from pg_statistic.
-		 */
-		if (tle->expr && IsA(tle->expr, Var))
+		Assert(IsA(var, Var));
+
+		/* The width probably hasn't been cached yet, but may as well check */
+		if (rel->attr_widths[ndx] > 0)
 		{
-			Var		   *var = (Var *) tle->expr;
-			Oid			relid;
+				tuple_width += rel->attr_widths[ndx];
+				continue;
+		}
 
-			relid = getrelid(var->varno, root->rtable);
-			if (relid != InvalidOid)
+		relid = getrelid(var->varno, root->rtable);
+		if (relid != InvalidOid)
+		{
+			item_width = get_attavgwidth(relid, var->varattno);
+			if (item_width > 0)
 			{
-				item_width = get_attavgwidth(relid, var->varattno);
-				if (item_width > 0)
-				{
-					tuple_width += item_width;
-					continue;
-				}
+				rel->attr_widths[ndx] = item_width;
+				tuple_width += item_width;
+				continue;
 			}
 		}
 
 		/*
-		 * Not a Var, or can't find statistics for it.  Estimate using
-		 * just the type info.
+		 * Not a plain relation, or can't find statistics for it.
+		 * Estimate using just the type info.
 		 */
-		item_width = get_typavgwidth(tle->resdom->restype,
-									 tle->resdom->restypmod);
+		item_width = get_typavgwidth(var->vartype, var->vartypmod);
 		Assert(item_width > 0);
+		rel->attr_widths[ndx] = item_width;
 		tuple_width += item_width;
 	}
 	Assert(tuple_width >= 0);
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index f8e4906c13bda67fb883585827b910f8b45874fc..d4d1179d54576d49ebe9346937942fbb265344f3 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.49 2003/05/28 16:03:56 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.50 2003/06/29 23:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -703,20 +703,18 @@ find_indexkey_var(Query *root, RelOptInfo *rel, AttrNumber varattno)
 				vartypeid;
 	int32		type_mod;
 
-	foreach(temp, rel->targetlist)
+	foreach(temp, FastListValue(&rel->reltargetlist))
 	{
-		Var		   *tle_var = (Var *) ((TargetEntry *) lfirst(temp))->expr;
+		Var		   *var = (Var *) lfirst(temp);
 
-		if (IsA(tle_var, Var) &&
-			tle_var->varattno == varattno)
-			return tle_var;
+		if (IsA(var, Var) &&
+			var->varattno == varattno)
+			return var;
 	}
 
 	relid = rel->relid;
-	Assert(relid > 0);
 	reloid = getrelid(relid, root->rtable);
-	vartypeid = get_atttype(reloid, varattno);
-	type_mod = get_atttypmod(reloid, varattno);
+	get_atttypetypmod(reloid, varattno, &vartypeid, &type_mod);
 
 	return makeVar(relid, varattno, vartypeid, type_mod, 0);
 }
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 81ee7962df90a5c4193d871909cebcd12542c4d6..1cdc3628b20ceb718b4e380e66232c12e77b1076 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.146 2003/06/16 02:03:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.147 2003/06/29 23:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,7 @@
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/paths.h"
+#include "optimizer/plancat.h"
 #include "optimizer/planmain.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
@@ -34,6 +35,7 @@
 
 
 static Scan *create_scan_plan(Query *root, Path *best_path);
+static List *build_relation_tlist(RelOptInfo *rel);
 static bool use_physical_tlist(RelOptInfo *rel);
 static void disuse_physical_tlist(Plan *plan, Path *path);
 static Join *create_join_plan(Query *root, JoinPath *best_path);
@@ -199,20 +201,13 @@ create_scan_plan(Query *root, Path *best_path)
 	 */
 	if (use_physical_tlist(rel))
 	{
-		int		resdomno = 1;
-		List   *v;
-
-		tlist = NIL;
-		foreach(v, rel->varlist)
-		{
-			Var	   *var = (Var *) lfirst(v);
-
-			tlist = lappend(tlist, create_tl_element(var, resdomno));
-			resdomno++;
-		}
+		tlist = build_physical_tlist(root, rel);
+		/* if fail because of dropped cols, use regular method */
+		if (tlist == NIL)
+			tlist = build_relation_tlist(rel);
 	}
 	else
-		tlist = rel->targetlist;
+		tlist = build_relation_tlist(rel);
 
 	/*
 	 * Extract the relevant restriction clauses from the parent relation;
@@ -266,6 +261,28 @@ create_scan_plan(Query *root, Path *best_path)
 	return plan;
 }
 
+/*
+ * Build a target list (ie, a list of TargetEntry) for a relation.
+ */
+static List *
+build_relation_tlist(RelOptInfo *rel)
+{
+	FastList	tlist;
+	int			resdomno = 1;
+	List	   *v;
+
+	FastListInit(&tlist);
+	foreach(v, FastListValue(&rel->reltargetlist))
+	{
+		/* Do we really need to copy here?  Not sure */
+		Var	   *var = (Var *) copyObject(lfirst(v));
+
+		FastAppend(&tlist, create_tl_element(var, resdomno));
+		resdomno++;
+	}
+	return FastListValue(&tlist);
+}
+
 /*
  * use_physical_tlist
  *		Decide whether to use a tlist matching relation structure,
@@ -274,12 +291,12 @@ create_scan_plan(Query *root, Path *best_path)
 static bool
 use_physical_tlist(RelOptInfo *rel)
 {
-	List	   *t;
+	int			i;
 
 	/*
 	 * Currently, can't do this for subquery or function scans.  (This
-	 * is mainly because we don't set up the necessary info when creating
-	 * their RelOptInfo nodes.)
+	 * is mainly because we don't have an equivalent of build_physical_tlist
+	 * for them; worth adding?)
 	 */
 	if (rel->rtekind != RTE_RELATION)
 		return false;
@@ -289,26 +306,15 @@ use_physical_tlist(RelOptInfo *rel)
 	 */
 	if (rel->reloptkind != RELOPT_BASEREL)
 		return false;
-	/*
-	 * Can't do it if relation contains dropped columns.  This is detected
-	 * in plancat.c, see notes there.
-	 */
-	if (rel->varlist == NIL)
-		return false;
 	/*
 	 * Can't do it if any system columns are requested, either.  (This could
 	 * possibly be fixed but would take some fragile assumptions in setrefs.c,
 	 * I think.)
 	 */
-	foreach(t, rel->targetlist)
+	for (i = rel->min_attr; i <= 0; i++)
 	{
-		TargetEntry *tle = (TargetEntry *) lfirst(t);
-		Var		   *var = (Var *) tle->expr;
-
-		if (!var || !IsA(var, Var))
-			return false;		/* probably can't happen */
-		if (var->varattno <= 0)
-			return false;		/* system column! */
+		if (!bms_is_empty(rel->attr_needed[i - rel->min_attr]))
+			return false;
 	}
 	return true;
 }
@@ -333,7 +339,7 @@ disuse_physical_tlist(Plan *plan, Path *path)
 		case T_TidScan:
 		case T_SubqueryScan:
 		case T_FunctionScan:
-			plan->targetlist = path->parent->targetlist;
+			plan->targetlist = build_relation_tlist(path->parent);
 			break;
 		default:
 			break;
@@ -411,7 +417,7 @@ static Append *
 create_append_plan(Query *root, AppendPath *best_path)
 {
 	Append	   *plan;
-	List	   *tlist = best_path->path.parent->targetlist;
+	List	   *tlist = build_relation_tlist(best_path->path.parent);
 	List	   *subplans = NIL;
 	List	   *subpaths;
 
@@ -443,7 +449,7 @@ create_result_plan(Query *root, ResultPath *best_path)
 	Plan	   *subplan;
 
 	if (best_path->path.parent)
-		tlist = best_path->path.parent->targetlist;
+		tlist = build_relation_tlist(best_path->path.parent);
 	else
 		tlist = NIL;			/* will be filled in later */
 
@@ -842,7 +848,7 @@ create_nestloop_plan(Query *root,
 					 Plan *outer_plan,
 					 Plan *inner_plan)
 {
-	List	   *tlist = best_path->path.parent->targetlist;
+	List	   *tlist = build_relation_tlist(best_path->path.parent);
 	List	   *joinrestrictclauses = best_path->joinrestrictinfo;
 	List	   *joinclauses;
 	List	   *otherclauses;
@@ -912,7 +918,7 @@ create_mergejoin_plan(Query *root,
 					  Plan *outer_plan,
 					  Plan *inner_plan)
 {
-	List	   *tlist = best_path->jpath.path.parent->targetlist;
+	List	   *tlist = build_relation_tlist(best_path->jpath.path.parent);
 	List	   *joinclauses;
 	List	   *otherclauses;
 	List	   *mergeclauses;
@@ -992,7 +998,7 @@ create_hashjoin_plan(Query *root,
 					 Plan *outer_plan,
 					 Plan *inner_plan)
 {
-	List	   *tlist = best_path->jpath.path.parent->targetlist;
+	List	   *tlist = build_relation_tlist(best_path->jpath.path.parent);
 	List	   *joinclauses;
 	List	   *otherclauses;
 	List	   *hashclauses;
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 05d963d94c10f8bb4c3e0a7e3d15995f34210238..cbc1ff365a31b9ddd7c99c91d5248ebdfeb17bc8 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.85 2003/03/02 23:46:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.86 2003/06/29 23:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,7 +40,8 @@ static void distribute_qual_to_rels(Query *root, Node *clause,
 									bool isdeduced,
 									Relids outerjoin_nonnullable,
 									Relids qualscope);
-static void add_vars_to_targetlist(Query *root, List *vars);
+static void add_vars_to_targetlist(Query *root, List *vars,
+								   Relids where_needed);
 static bool qual_is_redundant(Query *root, RestrictInfo *restrictinfo,
 				  List *restrictlist);
 static void check_mergejoinable(RestrictInfo *restrictinfo);
@@ -112,34 +113,54 @@ add_base_rels_to_query(Query *root, Node *jtnode)
 
 /*
  * build_base_rel_tlists
- *	  Creates targetlist entries for each var seen in 'tlist' and adds
- *	  them to the tlist of the appropriate rel node.
+ *	  Add targetlist entries for each var needed in the query's final tlist
+ *	  to the appropriate base relations.
+ *
+ * We mark such vars as needed by "relation 0" to ensure that they will
+ * propagate up through all join plan steps.
  */
 void
-build_base_rel_tlists(Query *root, List *tlist)
+build_base_rel_tlists(Query *root, List *final_tlist)
 {
-	List	   *tlist_vars = pull_var_clause((Node *) tlist, false);
+	List	   *tlist_vars = pull_var_clause((Node *) final_tlist, false);
 
-	add_vars_to_targetlist(root, tlist_vars);
-	freeList(tlist_vars);
+	if (tlist_vars != NIL)
+	{
+		add_vars_to_targetlist(root, tlist_vars, bms_make_singleton(0));
+		freeList(tlist_vars);
+	}
 }
 
 /*
  * add_vars_to_targetlist
  *	  For each variable appearing in the list, add it to the owning
- *	  relation's targetlist if not already present.
+ *	  relation's targetlist if not already present, and mark the variable
+ *	  as being needed for the indicated join (or for final output if
+ *	  where_needed includes "relation 0").
  */
 static void
-add_vars_to_targetlist(Query *root, List *vars)
+add_vars_to_targetlist(Query *root, List *vars, Relids where_needed)
 {
 	List	   *temp;
 
+	Assert(!bms_is_empty(where_needed));
+
 	foreach(temp, vars)
 	{
 		Var		   *var = (Var *) lfirst(temp);
 		RelOptInfo *rel = find_base_rel(root, var->varno);
+		int			attrno = var->varattno;
 
-		add_var_to_tlist(rel, var);
+		Assert(attrno >= rel->min_attr && attrno <= rel->max_attr);
+		attrno -= rel->min_attr;
+		if (bms_is_empty(rel->attr_needed[attrno]))
+		{
+			/* Variable not yet requested, so add to reltargetlist */
+			/* XXX is copyObject necessary here? */
+			FastAppend(&rel->reltargetlist, copyObject(var));
+		}
+		rel->attr_needed[attrno] = bms_add_members(rel->attr_needed[attrno],
+												   where_needed);
 	}
 }
 
@@ -575,7 +596,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
 			 * scan those relations (else they won't be available at the join
 			 * node!).
 			 */
-			add_vars_to_targetlist(root, vars);
+			add_vars_to_targetlist(root, vars, relids);
 			break;
 		default:
 			/*
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index ac6e0f0f35225e3600d53a0437a11f25c08088b2..03674e8990f8088835267b187a7d7ff44cdaf85f 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.96 2003/06/16 02:03:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.97 2003/06/29 23:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -800,6 +800,7 @@ adjust_inherited_attrs_mutator(Node *node,
 			var->varno == context->old_rt_index)
 		{
 			var->varno = context->new_rt_index;
+			var->varnoold = context->new_rt_index;
 			if (var->varattno > 0)
 			{
 				char	   *attname = get_attname(context->old_relid,
@@ -809,6 +810,7 @@ adjust_inherited_attrs_mutator(Node *node,
 				if (var->varattno == InvalidAttrNumber)
 					elog(ERROR, "Relation \"%s\" has no column \"%s\"",
 						 get_rel_name(context->new_relid), attname);
+				var->varoattno = var->varattno;
 				pfree(attname);
 			}
 		}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index da9497d58a4c5691320392230da12284acf51bdb..9e919079ccf9c2d6d1f18ee34c6bb2a70259826e 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.90 2003/06/15 22:51:45 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.91 2003/06/29 23:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -581,7 +581,7 @@ create_unique_path(Query *root, RelOptInfo *rel, Path *subpath)
 	else
 	{
 		pathnode->rows = rel->rows;
-		numCols = length(rel->targetlist); /* second-best estimate */
+		numCols = length(FastListValue(&rel->reltargetlist));
 	}
 
 	/*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 36223be93fdc823cc088d742b6588eb7f2a5b567..3f4fe661717dbd405a971aac095fcfdc1e20df89 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.83 2003/05/28 16:03:56 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.84 2003/06/29 23:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,6 +26,7 @@
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/plancat.h"
+#include "optimizer/tlist.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/builtins.h"
@@ -44,7 +45,8 @@
  * Given the Oid of the relation, return the following info into fields
  * of the RelOptInfo struct:
  *
- *	varlist		list of physical columns (expressed as Vars)
+ *	min_attr	lowest valid AttrNumber
+ *	max_attr	highest valid AttrNumber
  *	indexlist	list of IndexOptInfos for relation's indexes
  *	pages		number of pages
  *	tuples		number of tuples
@@ -52,49 +54,15 @@
 void
 get_relation_info(Oid relationObjectId, RelOptInfo *rel)
 {
-	Relation	relation;
 	Index		varno = rel->relid;
+	Relation	relation;
 	bool		hasindex;
-	List	   *varlist = NIL;
 	List	   *indexinfos = NIL;
-	int			attrno,
-				numattrs;
 
 	relation = heap_open(relationObjectId, AccessShareLock);
 
-	/*
-	 * Make list of physical Vars.  But if there are any dropped columns,
-	 * punt and set varlist to NIL.  (XXX Ideally we would like to include
-	 * dropped columns so that the varlist models the physical tuples
-	 * of the relation.  However this creates problems for ExecTypeFromTL,
-	 * which may be asked to build a tupdesc for a tlist that includes vars
-	 * of no-longer-existent types.  In theory we could dig out the required
-	 * info from the pg_attribute entries of the relation, but that data is
-	 * not readily available to ExecTypeFromTL.  For now, punt and don't
-	 * apply the physical-tlist optimization when there are dropped cols.)
-	 */
-	numattrs = RelationGetNumberOfAttributes(relation);
-
-	for (attrno = 1; attrno <= numattrs; attrno++)
-	{
-		Form_pg_attribute att_tup = relation->rd_att->attrs[attrno - 1];
-
-		if (att_tup->attisdropped)
-		{
-			/* found a dropped col, so punt */
-			varlist = NIL;
-			break;
-		}
-
-		varlist = lappend(varlist,
-						  makeVar(varno,
-								  attrno,
-								  att_tup->atttypid,
-								  att_tup->atttypmod,
-								  0));
-	}
-
-	rel->varlist = varlist;
+	rel->min_attr = FirstLowInvalidHeapAttributeNumber + 1;
+	rel->max_attr = RelationGetNumberOfAttributes(relation);
 
 	/*
 	 * Make list of indexes.  Ignore indexes on system catalogs if told to.
@@ -199,6 +167,65 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel)
 	heap_close(relation, AccessShareLock);
 }
 
+/*
+ * build_physical_tlist
+ *
+ * Build a targetlist consisting of exactly the relation's user attributes,
+ * in order.  The executor can special-case such tlists to avoid a projection
+ * step at runtime, so we use such tlists preferentially for scan nodes.
+ *
+ * Exception: if there are any dropped columns, we punt and return NIL.
+ * Ideally we would like to handle the dropped-column case too.  However this
+ * creates problems for ExecTypeFromTL, which may be asked to build a tupdesc
+ * for a tlist that includes vars of no-longer-existent types.  In theory we
+ * could dig out the required info from the pg_attribute entries of the
+ * relation, but that data is not readily available to ExecTypeFromTL.
+ * For now, we don't apply the physical-tlist optimization when there are
+ * dropped cols.
+ */
+List *
+build_physical_tlist(Query *root, RelOptInfo *rel)
+{
+	Index		varno = rel->relid;
+	RangeTblEntry *rte = rt_fetch(varno, root->rtable);
+	Relation	relation;
+	FastList	tlist;
+	int			attrno,
+				numattrs;
+
+	FastListInit(&tlist);
+
+	Assert(rte->rtekind == RTE_RELATION);
+
+	relation = heap_open(rte->relid, AccessShareLock);
+
+	numattrs = RelationGetNumberOfAttributes(relation);
+
+	for (attrno = 1; attrno <= numattrs; attrno++)
+	{
+		Form_pg_attribute att_tup = relation->rd_att->attrs[attrno - 1];
+
+		if (att_tup->attisdropped)
+		{
+			/* found a dropped col, so punt */
+			FastListInit(&tlist);
+			break;
+		}
+
+		FastAppend(&tlist,
+				   create_tl_element(makeVar(varno,
+											 attrno,
+											 att_tup->atttypid,
+											 att_tup->atttypmod,
+											 0),
+									 attrno));
+	}
+
+	heap_close(relation, AccessShareLock);
+
+	return FastListValue(&tlist);
+}
+
 /*
  * restriction_selectivity
  *
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index af44cb7f20616aa9a493c426332b2f72518cc53a..0b82569ba375ebde7478a436837aa1d09663e860 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.48 2003/02/15 20:12:40 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.49 2003/06/29 23:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,7 +24,7 @@
 
 
 static RelOptInfo *make_base_rel(Query *root, int relid);
-static List *new_join_tlist(List *tlist, int first_resdomno);
+static void build_joinrel_tlist(Query *root, RelOptInfo *joinrel);
 static List *build_joinrel_restrictlist(Query *root,
 						   RelOptInfo *joinrel,
 						   RelOptInfo *outer_rel,
@@ -130,7 +130,7 @@ make_base_rel(Query *root, int relid)
 	rel->relids = bms_make_singleton(relid);
 	rel->rows = 0;
 	rel->width = 0;
-	rel->targetlist = NIL;
+	FastListInit(&rel->reltargetlist);
 	rel->pathlist = NIL;
 	rel->cheapest_startup_path = NULL;
 	rel->cheapest_total_path = NULL;
@@ -138,7 +138,7 @@ make_base_rel(Query *root, int relid)
 	rel->pruneable = true;
 	rel->relid = relid;
 	rel->rtekind = rte->rtekind;
-	rel->varlist = NIL;
+	/* min_attr, max_attr, attr_needed, attr_widths are set below */
 	rel->indexlist = NIL;
 	rel->pages = 0;
 	rel->tuples = 0;
@@ -160,7 +160,9 @@ make_base_rel(Query *root, int relid)
 			break;
 		case RTE_SUBQUERY:
 		case RTE_FUNCTION:
-			/* Subquery or function --- nothing to do here */
+			/* Subquery or function --- need only set up attr range */
+			rel->min_attr = 1;
+			rel->max_attr = length(rte->eref->colnames);
 			break;
 		default:
 			elog(ERROR, "make_base_rel: unsupported RTE kind %d",
@@ -168,6 +170,19 @@ make_base_rel(Query *root, int relid)
 			break;
 	}
 
+	if (rel->max_attr >= rel->min_attr)
+	{
+		rel->attr_needed = (Relids *)
+			palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(Relids));
+		rel->attr_widths = (int32 *)
+			palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(int32));
+	}
+	else
+	{
+		rel->attr_needed = NULL;
+		rel->attr_widths = NULL;
+	}
+
 	return rel;
 }
 
@@ -252,8 +267,6 @@ build_join_rel(Query *root,
 {
 	RelOptInfo *joinrel;
 	List	   *restrictlist;
-	List	   *new_outer_tlist;
-	List	   *new_inner_tlist;
 
 	/*
 	 * See if we already have a joinrel for this set of base rels.
@@ -283,7 +296,7 @@ build_join_rel(Query *root,
 	joinrel->relids = bms_copy(joinrelids);
 	joinrel->rows = 0;
 	joinrel->width = 0;
-	joinrel->targetlist = NIL;
+	FastListInit(&joinrel->reltargetlist);
 	joinrel->pathlist = NIL;
 	joinrel->cheapest_startup_path = NULL;
 	joinrel->cheapest_total_path = NULL;
@@ -291,7 +304,10 @@ build_join_rel(Query *root,
 	joinrel->pruneable = true;
 	joinrel->relid = 0;			/* indicates not a baserel */
 	joinrel->rtekind = RTE_JOIN;
-	joinrel->varlist = NIL;
+	joinrel->min_attr = 0;
+	joinrel->max_attr = 0;
+	joinrel->attr_needed = NULL;
+	joinrel->attr_widths = NULL;
 	joinrel->indexlist = NIL;
 	joinrel->pages = 0;
 	joinrel->tuples = 0;
@@ -305,24 +321,10 @@ build_join_rel(Query *root,
 	joinrel->index_inner_paths = NIL;
 
 	/*
-	 * Create a new tlist by removing irrelevant elements from both tlists
-	 * of the outer and inner join relations and then merging the results
-	 * together.
-	 *
-	 * XXX right now we don't remove any irrelevant elements, we just append
-	 * the two tlists together.  Someday consider pruning vars from the
-	 * join's targetlist if they are needed only to evaluate restriction
-	 * clauses of this join, and will never be accessed at higher levels
-	 * of the plantree.
-	 *
-	 * NOTE: the tlist order for a join rel will depend on which pair of
-	 * outer and inner rels we first try to build it from.	But the
-	 * contents should be the same regardless.
+	 * Create a new tlist containing just the vars that need to be output
+	 * from this join (ie, are needed for higher joinclauses or final output).
 	 */
-	new_outer_tlist = new_join_tlist(outer_rel->targetlist, 1);
-	new_inner_tlist = new_join_tlist(inner_rel->targetlist,
-									 length(new_outer_tlist) + 1);
-	joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist);
+	build_joinrel_tlist(root, joinrel);
 
 	/*
 	 * Construct restrict and join clause lists for the new joinrel. (The
@@ -353,42 +355,51 @@ build_join_rel(Query *root,
 }
 
 /*
- * new_join_tlist
- *	  Builds a join relation's target list by keeping those elements that
- *	  will be in the final target list and any other elements that are still
- *	  needed for future joins.	For a target list entry to still be needed
- *	  for future joins, its 'joinlist' field must not be empty after removal
- *	  of all relids in 'other_relids'.
+ * build_joinrel_tlist
+ *	  Builds a join relation's target list.
  *
- *	  XXX the above comment refers to code that is long dead and gone;
- *	  we don't keep track of joinlists for individual targetlist entries
- *	  anymore.	For now, all vars present in either input tlist will be
- *	  emitted in the join's tlist.
+ * The join's targetlist includes all Vars of its member relations that
+ * will still be needed above the join.
  *
- * 'tlist' is the target list of one of the join relations
- * 'first_resdomno' is the resdom number to use for the first created
- *				target list entry
+ * In a former lifetime, this just merged the tlists of the two member
+ * relations first presented.  While we could still do that, working from
+ * lists of Vars would mean doing a find_base_rel lookup for each Var.
+ * It seems more efficient to scan the list of base rels and collect the
+ * needed vars directly from there.
  *
- * Returns the new target list.
+ * We also compute the expected width of the join's output, making use
+ * of data that was cached at the baserel level by set_rel_width().
  */
-static List *
-new_join_tlist(List *tlist,
-			   int first_resdomno)
+static void
+build_joinrel_tlist(Query *root, RelOptInfo *joinrel)
 {
-	int			resdomno = first_resdomno - 1;
-	List	   *t_list = NIL;
-	List	   *i;
+	Relids		relids = joinrel->relids;
+	List	   *rels;
+	List	   *vars;
+
+	FastListInit(&joinrel->reltargetlist);
+	joinrel->width = 0;
 
-	foreach(i, tlist)
+	foreach(rels, root->base_rel_list)
 	{
-		TargetEntry *tle = lfirst(i);
+		RelOptInfo *baserel = (RelOptInfo *) lfirst(rels);
 
-		resdomno += 1;
-		t_list = lappend(t_list,
-						 create_tl_element((Var *) tle->expr, resdomno));
-	}
+		if (!bms_is_member(baserel->relid, relids))
+			continue;
 
-	return t_list;
+		foreach(vars, FastListValue(&baserel->reltargetlist))
+		{
+			Var	   *var = (Var *) lfirst(vars);
+			int		ndx = var->varattno - baserel->min_attr;
+
+			if (bms_nonempty_difference(baserel->attr_needed[ndx], relids))
+			{
+				FastAppend(&joinrel->reltargetlist, var);
+				Assert(baserel->attr_widths[ndx] > 0);
+				joinrel->width += baserel->attr_widths[ndx];
+			}
+		}
+	}
 }
 
 /*
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 9b10e8e97be34fc03b09c6460a458570e25472b9..7a4422a3594195a30a53b5ba24b8d25b5a994810 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.56 2003/05/06 00:20:32 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.57 2003/06/29 23:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,7 @@
 
 
 /*****************************************************************************
- *	---------- RELATION node target list routines ----------
+ *		Target list creation and searching utilities
  *****************************************************************************/
 
 /*
@@ -79,24 +79,6 @@ tlist_member(Node *node, List *targetlist)
 	return (Resdom *) NULL;
 }
 
-/*
- * add_var_to_tlist
- *	  Creates a targetlist entry corresponding to the supplied var node
- *	  'var' and adds the new targetlist entry to the targetlist field of
- *	  'rel'.  No entry is created if 'var' is already in the tlist.
- */
-void
-add_var_to_tlist(RelOptInfo *rel, Var *var)
-{
-	if (!tlistentry_member((Node *) var, rel->targetlist))
-	{
-		/* XXX is copyObject necessary here? */
-		rel->targetlist = lappend(rel->targetlist,
-							   create_tl_element((Var *) copyObject(var),
-										   length(rel->targetlist) + 1));
-	}
-}
-
 /*
  * create_tl_element
  *	  Creates a target list entry node and its associated (resdom var) pair
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 4e06d7a284a9137d3917f2987fb68725bb1a687d..7974c44aae78540fb4de5320f37b65363d692a18 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -13,7 +13,7 @@
  *
  * Copyright (c) 2003, PostgreSQL Global Development Group
  *
- * $Id: bitmapset.h,v 1.1 2003/02/08 20:20:55 tgl Exp $
+ * $Id: bitmapset.h,v 1.2 2003/06/29 23:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,6 +59,7 @@ extern Bitmapset *bms_difference(const Bitmapset *a, const Bitmapset *b);
 extern bool bms_is_subset(const Bitmapset *a, const Bitmapset *b);
 extern bool bms_is_member(int x, const Bitmapset *a);
 extern bool bms_overlap(const Bitmapset *a, const Bitmapset *b);
+extern bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b);
 extern int	bms_singleton_member(const Bitmapset *a);
 extern int	bms_num_members(const Bitmapset *a);
 /* optimized tests when we don't need to know exact membership count: */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 8392ab505bd66789c88a1d8aa4ff680cf265a40f..2ab9e0f6e7f7062c81902f46cbb6daeeca545a09 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.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: relation.h,v 1.81 2003/06/15 22:51:45 tgl Exp $
+ * $Id: relation.h,v 1.82 2003/06/29 23:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -89,8 +89,8 @@ typedef struct QualCost
  *			   clauses have been applied (ie, output rows of a plan for it)
  *		width - avg. number of bytes per tuple in the relation after the
  *				appropriate projections have been done (ie, output width)
- *		targetlist - List of TargetEntry nodes for the attributes we need
- *					 to output from this relation
+ *		reltargetlist - List of Var nodes for the attributes we need to
+ *						output from this relation (in no particular order)
  *		pathlist - List of Path nodes, one for each potentially useful
  *				   method of generating the relation
  *		cheapest_startup_path - the pathlist member with lowest startup cost
@@ -107,7 +107,12 @@ typedef struct QualCost
  *		relid - RTE index (this is redundant with the relids field, but
  *				is provided for convenience of access)
  *		rtekind - distinguishes plain relation, subquery, or function RTE
- *		varlist - list of Vars for physical columns (only if table)
+ *		min_attr, max_attr - range of valid AttrNumbers for rel
+ *		attr_needed - array of bitmapsets indicating the highest joinrel
+ *				in which each attribute is needed; if bit 0 is set then
+ *				the attribute is needed as part of final targetlist
+ *		attr_widths - cache space for per-attribute width estimates;
+ *					  zero means not computed yet
  *		indexlist - list of IndexOptInfo nodes for relation's indexes
  *					(always NIL if it's not a table)
  *		pages - number of disk pages in relation (zero if not a table)
@@ -183,7 +188,7 @@ typedef struct RelOptInfo
 	int			width;			/* estimated avg width of result tuples */
 
 	/* materialization information */
-	List	   *targetlist;
+	FastList	reltargetlist;
 	List	   *pathlist;		/* Path structures */
 	struct Path *cheapest_startup_path;
 	struct Path *cheapest_total_path;
@@ -193,7 +198,10 @@ typedef struct RelOptInfo
 	/* information about a base rel (not set for join rels!) */
 	Index		relid;
 	RTEKind		rtekind;		/* RELATION, SUBQUERY, or FUNCTION */
-	List	   *varlist;
+	AttrNumber	min_attr;		/* smallest attrno of rel (often <0) */
+	AttrNumber	max_attr;		/* largest attrno of rel */
+	Relids	   *attr_needed;	/* array indexed [min_attr .. max_attr] */
+	int32	   *attr_widths;	/* array indexed [min_attr .. max_attr] */
 	List	   *indexlist;
 	long		pages;
 	double		tuples;
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index c6b18ab0da9463bc06cc47c143c63441294ac106..8f7cfbe6ceb67e98040ea648b687b46279928955 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.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: plancat.h,v 1.29 2003/02/03 15:07:08 tgl Exp $
+ * $Id: plancat.h,v 1.30 2003/06/29 23:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,6 +19,8 @@
 
 extern void get_relation_info(Oid relationObjectId, RelOptInfo *rel);
 
+extern List *build_physical_tlist(Query *root, RelOptInfo *rel);
+
 extern List *find_inheritance_children(Oid inhparent);
 
 extern bool has_subclass(Oid relationId);
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 106e9d1ce4b549cc4fe7ef873660dabe2bb59b6d..99c9470493df5a3995a8092fa60b57f1c2637b0c 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.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: planmain.h,v 1.71 2003/06/29 00:33:44 tgl Exp $
+ * $Id: planmain.h,v 1.72 2003/06/29 23:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,7 +56,7 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
  * prototypes for plan/initsplan.c
  */
 extern void add_base_rels_to_query(Query *root, Node *jtnode);
-extern void build_base_rel_tlists(Query *root, List *tlist);
+extern void build_base_rel_tlists(Query *root, List *final_tlist);
 extern Relids distribute_quals_to_rels(Query *root, Node *jtnode);
 extern void process_implied_equality(Query *root,
 									 Node *item1, Node *item2,
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index e2afc3ac82192fbb2074bb78e14628039aaef964..d22c78f8d8e694cca8f9c822e96792d10ee895a5 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.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: tlist.h,v 1.35 2003/05/06 00:20:33 tgl Exp $
+ * $Id: tlist.h,v 1.36 2003/06/29 23:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,10 +16,10 @@
 
 #include "nodes/relation.h"
 
+
 extern TargetEntry *tlistentry_member(Node *node, List *targetlist);
 extern Resdom *tlist_member(Node *node, List *targetlist);
 
-extern void add_var_to_tlist(RelOptInfo *rel, Var *var);
 extern TargetEntry *create_tl_element(Var *var, int resdomno);
 
 extern List *flatten_tlist(List *tlist);