diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
index 54b913dcac1..073360c1e4a 100644
--- a/src/backend/commands/command.c
+++ b/src/backend/commands/command.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.109 2000/11/08 22:09:57 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.110 2000/11/12 00:36:56 tgl Exp $
  *
  * NOTES
  *	  The PerformAddAttribute() code, like most of the relation
@@ -1098,13 +1098,12 @@ AlterTableAddConstraint(char *relationName,
 				case CONSTR_CHECK:
 				{
 					ParseState *pstate;
-					bool successful = TRUE;
+					bool successful = true;
 					HeapScanDesc scan;
 					ExprContext *econtext;
 					TupleTableSlot *slot = makeNode(TupleTableSlot);
 					HeapTuple tuple;
 					RangeTblEntry *rte;
-					List       *rtlist;
 					List       *qual;
 					List       *constlist;
 					Relation	rel;
@@ -1112,9 +1111,9 @@ AlterTableAddConstraint(char *relationName,
 					char *name;
 
 					if (constr->name)
-						name=constr->name;
+						name = constr->name;
 					else
-						name="<unnamed>";
+						name = "<unnamed>";
 
 					constlist = makeList1(constr);
 
@@ -1169,13 +1168,6 @@ AlterTableAddConstraint(char *relationName,
 
 					qual = makeList1(expr);
 
-					rte = makeNode(RangeTblEntry);
-					rte->relname = relationName;
-					rte->relid = RelationGetRelid(rel);
-					rte->eref = makeNode(Attr);
-					rte->eref->relname = relationName;
-					rtlist = makeList1(rte);
-
 					/*
 					 * Scan through the rows now, making the necessary things
 					 * for ExecQual, and then call it to evaluate the
@@ -1188,10 +1180,8 @@ AlterTableAddConstraint(char *relationName,
 						slot->ttc_descIsNew = true;
 						slot->ttc_tupleDescriptor = rel->rd_att;
 						slot->ttc_buffer = InvalidBuffer;
-						slot->ttc_whichplan = -1;
 
 						econtext = MakeExprContext(slot, CurrentMemoryContext);
-						econtext->ecxt_range_table = rtlist; /* range table */
 						if (!ExecQual(qual, econtext, true))
 						{
 							successful=false;
@@ -1201,8 +1191,6 @@ AlterTableAddConstraint(char *relationName,
 					}
 
 					pfree(slot);
-					pfree(rtlist);
-					pfree(rte);
 
 					heap_endscan(scan);
 					heap_close(rel, NoLock);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index ea90e0f2e04..2877999500c 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.122 2000/09/06 14:15:16 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.123 2000/11/12 00:36:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -597,7 +597,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 	int32		ntuples,
 				tuples_read = 0;
 	bool		reading_to_eof = true;
-	RelationInfo *relationInfo;
+	ResultRelInfo *resultRelInfo;
 	EState	   *estate = CreateExecutorState();	/* for ExecConstraints() */
 	TupleTable	tupleTable;
 	TupleTableSlot *slot;
@@ -609,20 +609,19 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 	attr_count = tupDesc->natts;
 
 	/*
-	 * We need a RelationInfo so we can use the regular executor's
+	 * We need a ResultRelInfo so we can use the regular executor's
 	 * index-entry-making machinery.  (There used to be a huge amount
 	 * of code here that basically duplicated execUtils.c ...)
 	 */
-	relationInfo = makeNode(RelationInfo);
-	relationInfo->ri_RangeTableIndex = 1; /* dummy */
-	relationInfo->ri_RelationDesc = rel;
-	relationInfo->ri_NumIndices = 0;
-	relationInfo->ri_IndexRelationDescs = NULL;
-	relationInfo->ri_IndexRelationInfo = NULL;
+	resultRelInfo = makeNode(ResultRelInfo);
+	resultRelInfo->ri_RangeTableIndex = 1; /* dummy */
+	resultRelInfo->ri_RelationDesc = rel;
 
-	ExecOpenIndices(relationInfo);
+	ExecOpenIndices(resultRelInfo);
 
-	estate->es_result_relation_info = relationInfo;
+	estate->es_result_relations = resultRelInfo;
+	estate->es_num_result_relations = 1;
+	estate->es_result_relation_info = resultRelInfo;
 
 	/* Set up a dummy tuple table too */
 	tupleTable = ExecCreateTupleTable(1);
@@ -830,7 +829,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 			ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
 			if (rel->rd_att->constr)
-				ExecConstraints("CopyFrom", rel, slot, estate);
+				ExecConstraints("CopyFrom", resultRelInfo, slot, estate);
 
 			/* ----------------
 			 * OK, store the tuple and create index entries for it
@@ -838,7 +837,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 			 */
 			heap_insert(rel, tuple);
 
-			if (relationInfo->ri_NumIndices > 0)
+			if (resultRelInfo->ri_NumIndices > 0)
 				ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
 
 			/* AFTER ROW INSERT Triggers */
@@ -886,7 +885,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 
 	ExecDropTupleTable(tupleTable, true);
 
-	ExecCloseIndices(relationInfo);
+	ExecCloseIndices(resultRelInfo);
 }
 
 
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 6976278c1d0..b76d45480f2 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.61 2000/10/26 21:34:44 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.62 2000/11/12 00:36:56 tgl Exp $
  *
  */
 
@@ -327,32 +327,18 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
 	if (IsA(plan, Append))
 	{
 		Append	   *appendplan = (Append *) plan;
-		List	   *saved_rtable = es->rtable;
-		int			whichplan = 0;
 		List	   *lst;
 
 		foreach(lst, appendplan->appendplans)
 		{
 			Plan	   *subnode = (Plan *) lfirst(lst);
 
-			if (appendplan->inheritrelid > 0)
-			{
-				RangeTblEntry *rtentry;
-
-				rtentry = nth(whichplan, appendplan->inheritrtable);
-				Assert(rtentry != NULL);
-				rt_store(appendplan->inheritrelid, es->rtable, rtentry);
-			}
-
 			for (i = 0; i < indent; i++)
 				appendStringInfo(str, "  ");
 			appendStringInfo(str, "  ->  ");
 
 			explain_outNode(str, subnode, indent + 3, es);
-
-			whichplan++;
 		}
-		es->rtable = saved_rtable;
 	}
 
 	if (IsA(plan, SubqueryScan))
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index a26acc9a763..d9d02654970 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -27,7 +27,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.131 2000/10/26 21:35:15 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.132 2000/11/12 00:36:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,6 +50,10 @@ static TupleDesc InitPlan(CmdType operation,
 		 Query *parseTree,
 		 Plan *plan,
 		 EState *estate);
+static void initResultRelInfo(ResultRelInfo *resultRelInfo,
+							  Index resultRelationIndex,
+							  List *rangeTable,
+							  CmdType operation);
 static void EndPlan(Plan *plan, EState *estate);
 static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
 								   CmdType operation,
@@ -310,10 +314,6 @@ ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
  * ExecCheckPlanPerms
  *		Recursively scan the plan tree to check access permissions in
  *		subplans.
- *
- * We also need to look at the local rangetables in Append plan nodes,
- * which is pretty bogus --- most likely, those tables should be mentioned
- * in the query's main rangetable.  But at the moment, they're not.
  */
 static void
 ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
@@ -365,27 +365,11 @@ ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
 				Append	   *app = (Append *) plan;
 				List	   *appendplans;
 
-				if (app->inheritrelid > 0)
+				foreach(appendplans, app->appendplans)
 				{
-					/* Append implements expansion of inheritance */
-					ExecCheckRTPerms(app->inheritrtable, operation);
-
-					foreach(appendplans, app->appendplans)
-					{
-						ExecCheckPlanPerms((Plan *) lfirst(appendplans),
-										   rangeTable,
-										   operation);
-					}
-				}
-				else
-				{
-					/* Append implements UNION, which must be a SELECT */
-					foreach(appendplans, app->appendplans)
-					{
-						ExecCheckPlanPerms((Plan *) lfirst(appendplans),
-										   rangeTable,
-										   CMD_SELECT);
-					}
+					ExecCheckPlanPerms((Plan *) lfirst(appendplans),
+									   rangeTable,
+									   operation);
 				}
 				break;
 			}
@@ -518,10 +502,8 @@ static TupleDesc
 InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 {
 	List	   *rangeTable;
-	int			resultRelation;
 	Relation	intoRelationDesc;
 	TupleDesc	tupType;
-	List	   *targetList;
 
 	/*
 	 * Do permissions checks.
@@ -532,7 +514,6 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 	 * get information from query descriptor
 	 */
 	rangeTable = parseTree->rtable;
-	resultRelation = parseTree->resultRelation;
 
 	/*
 	 * initialize the node's execution state
@@ -540,63 +521,61 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 	estate->es_range_table = rangeTable;
 
 	/*
-	 * initialize result relation stuff
+	 * if there is a result relation, initialize result relation stuff
 	 */
-
-	if (resultRelation != 0 && operation != CMD_SELECT)
+	if (parseTree->resultRelation != 0 && operation != CMD_SELECT)
 	{
+		List	   *resultRelations = parseTree->resultRelations;
+		int			numResultRelations;
+		ResultRelInfo *resultRelInfos;
 
-		/*
-		 * if we have a result relation, open it and initialize the result
-		 * relation info stuff.
-		 */
-		RelationInfo *resultRelationInfo;
-		Index		resultRelationIndex;
-		Oid			resultRelationOid;
-		Relation	resultRelationDesc;
+		if (resultRelations != NIL)
+		{
+			/*
+			 * Multiple result relations (due to inheritance)
+			 * parseTree->resultRelations identifies them all
+			 */
+			ResultRelInfo *resultRelInfo;
 
-		resultRelationIndex = resultRelation;
-		resultRelationOid = getrelid(resultRelationIndex, rangeTable);
-		resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock);
+			numResultRelations = length(resultRelations);
+			resultRelInfos = (ResultRelInfo *)
+				palloc(numResultRelations * sizeof(ResultRelInfo));
+			resultRelInfo = resultRelInfos;
+			while (resultRelations != NIL)
+			{
+				initResultRelInfo(resultRelInfo,
+								  lfirsti(resultRelations),
+								  rangeTable,
+								  operation);
+				resultRelInfo++;
+				resultRelations = lnext(resultRelations);
+			}
+		}
+		else
+		{
+			/*
+			 * Single result relation identified by parseTree->resultRelation
+			 */
+			numResultRelations = 1;
+			resultRelInfos = (ResultRelInfo *) palloc(sizeof(ResultRelInfo));
+			initResultRelInfo(resultRelInfos,
+							  parseTree->resultRelation,
+							  rangeTable,
+							  operation);
+		}
 
-		if (resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE)
-			elog(ERROR, "You can't change sequence relation %s",
-				 RelationGetRelationName(resultRelationDesc));
-
-		if (resultRelationDesc->rd_rel->relkind == RELKIND_TOASTVALUE)
-			elog(ERROR, "You can't change toast relation %s",
-				 RelationGetRelationName(resultRelationDesc));
-
-		if (resultRelationDesc->rd_rel->relkind == RELKIND_VIEW)
-			elog(ERROR, "You can't change view relation %s",
-				 RelationGetRelationName(resultRelationDesc));
-
-		resultRelationInfo = makeNode(RelationInfo);
-		resultRelationInfo->ri_RangeTableIndex = resultRelationIndex;
-		resultRelationInfo->ri_RelationDesc = resultRelationDesc;
-		resultRelationInfo->ri_NumIndices = 0;
-		resultRelationInfo->ri_IndexRelationDescs = NULL;
-		resultRelationInfo->ri_IndexRelationInfo = NULL;
-
-		/*
-		 * If there are indices on the result relation, open them and save
-		 * descriptors in the result relation info, so that we can add new
-		 * index entries for the tuples we add/update.	We need not do
-		 * this for a DELETE, however, since deletion doesn't affect
-		 * indexes.
-		 */
-		if (resultRelationDesc->rd_rel->relhasindex &&
-			operation != CMD_DELETE)
-			ExecOpenIndices(resultRelationInfo);
-
-		estate->es_result_relation_info = resultRelationInfo;
+		estate->es_result_relations = resultRelInfos;
+		estate->es_num_result_relations = numResultRelations;
+		/* Initialize to first or only result rel */
+		estate->es_result_relation_info = resultRelInfos;
 	}
 	else
 	{
-
 		/*
 		 * if no result relation, then set state appropriately
 		 */
+		estate->es_result_relations = NULL;
+		estate->es_num_result_relations = 0;
 		estate->es_result_relation_info = NULL;
 	}
 
@@ -642,19 +621,17 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 	ExecInitNode(plan, estate, NULL);
 
 	/*
-	 * get the tuple descriptor describing the type of tuples to return..
+	 * Get the tuple descriptor describing the type of tuples to return.
 	 * (this is especially important if we are creating a relation with
 	 * "retrieve into")
 	 */
 	tupType = ExecGetTupType(plan);		/* tuple descriptor */
-	targetList = plan->targetlist;
 
 	/*
-	 * Now that we have the target list, initialize the junk filter if
-	 * needed. SELECT and INSERT queries need a filter if there are any
-	 * junk attrs in the tlist.  UPDATE and DELETE always need one, since
-	 * there's always a junk 'ctid' attribute present --- no need to look
-	 * first.
+	 * Initialize the junk filter if needed. SELECT and INSERT queries need
+	 * a filter if there are any junk attrs in the tlist.  UPDATE and
+	 * DELETE always need one, since there's always a junk 'ctid' attribute
+	 * present --- no need to look first.
 	 */
 	{
 		bool		junk_filter_needed = false;
@@ -664,7 +641,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 		{
 			case CMD_SELECT:
 			case CMD_INSERT:
-				foreach(tlist, targetList)
+				foreach(tlist, plan->targetlist)
 				{
 					TargetEntry *tle = (TargetEntry *) lfirst(tlist);
 
@@ -685,12 +662,55 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 
 		if (junk_filter_needed)
 		{
-			JunkFilter *j = ExecInitJunkFilter(targetList, tupType);
+			/*
+			 * If there are multiple result relations, each one needs
+			 * its own junk filter.  Note this is only possible for
+			 * UPDATE/DELETE, so we can't be fooled by some needing
+			 * a filter and some not.
+			 */
+			if (parseTree->resultRelations != NIL)
+			{
+				List	   *subplans;
+				ResultRelInfo *resultRelInfo;
 
-			estate->es_junkFilter = j;
+				/* Top plan had better be an Append here. */
+				Assert(IsA(plan, Append));
+				Assert(((Append *) plan)->isTarget);
+				subplans = ((Append *) plan)->appendplans;
+				Assert(length(subplans) == estate->es_num_result_relations);
+				resultRelInfo = estate->es_result_relations;
+				while (subplans != NIL)
+				{
+					Plan	   *subplan = (Plan *) lfirst(subplans);
+					JunkFilter *j;
 
-			if (operation == CMD_SELECT)
-				tupType = j->jf_cleanTupType;
+					j = ExecInitJunkFilter(subplan->targetlist,
+										   ExecGetTupType(subplan));
+					resultRelInfo->ri_junkFilter = j;
+					resultRelInfo++;
+					subplans = lnext(subplans);
+				}
+				/*
+				 * Set active junkfilter too; at this point ExecInitAppend
+				 * has already selected an active result relation...
+				 */
+				estate->es_junkFilter =
+					estate->es_result_relation_info->ri_junkFilter;
+			}
+			else
+			{
+				/* Normal case with just one JunkFilter */
+				JunkFilter *j = ExecInitJunkFilter(plan->targetlist,
+												   tupType);
+
+				estate->es_junkFilter = j;
+				if (estate->es_result_relation_info)
+					estate->es_result_relation_info->ri_junkFilter = j;
+
+				/* For SELECT, want to return the cleaned tuple type */
+				if (operation == CMD_SELECT)
+					tupType = j->jf_cleanTupType;
+			}
 		}
 		else
 			estate->es_junkFilter = NULL;
@@ -762,6 +782,59 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 	return tupType;
 }
 
+/*
+ * Initialize ResultRelInfo data for one result relation
+ */
+static void
+initResultRelInfo(ResultRelInfo *resultRelInfo,
+				  Index resultRelationIndex,
+				  List *rangeTable,
+				  CmdType operation)
+{
+	Oid			resultRelationOid;
+	Relation	resultRelationDesc;
+
+	resultRelationOid = getrelid(resultRelationIndex, rangeTable);
+	resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock);
+
+	switch (resultRelationDesc->rd_rel->relkind)
+	{
+		case RELKIND_SEQUENCE:
+			elog(ERROR, "You can't change sequence relation %s",
+				 RelationGetRelationName(resultRelationDesc));
+			break;
+		case RELKIND_TOASTVALUE:
+			elog(ERROR, "You can't change toast relation %s",
+				 RelationGetRelationName(resultRelationDesc));
+			break;
+		case RELKIND_VIEW:
+			elog(ERROR, "You can't change view relation %s",
+				 RelationGetRelationName(resultRelationDesc));
+			break;
+	}
+
+	MemSet(resultRelInfo, 0, sizeof(ResultRelInfo));
+	resultRelInfo->type = T_ResultRelInfo;
+	resultRelInfo->ri_RangeTableIndex = resultRelationIndex;
+	resultRelInfo->ri_RelationDesc = resultRelationDesc;
+	resultRelInfo->ri_NumIndices = 0;
+	resultRelInfo->ri_IndexRelationDescs = NULL;
+	resultRelInfo->ri_IndexRelationInfo = NULL;
+	resultRelInfo->ri_ConstraintExprs = NULL;
+	resultRelInfo->ri_junkFilter = NULL;
+
+	/*
+	 * If there are indices on the result relation, open them and save
+	 * descriptors in the result relation info, so that we can add new
+	 * index entries for the tuples we add/update.	We need not do
+	 * this for a DELETE, however, since deletion doesn't affect
+	 * indexes.
+	 */
+	if (resultRelationDesc->rd_rel->relhasindex &&
+		operation != CMD_DELETE)
+		ExecOpenIndices(resultRelInfo);
+}
+
 /* ----------------------------------------------------------------
  *		EndPlan
  *
@@ -771,7 +844,8 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 static void
 EndPlan(Plan *plan, EState *estate)
 {
-	RelationInfo *resultRelationInfo;
+	ResultRelInfo *resultRelInfo;
+	int			i;
 	List	   *l;
 
 	/*
@@ -792,16 +866,16 @@ EndPlan(Plan *plan, EState *estate)
 	estate->es_tupleTable = NULL;
 
 	/*
-	 * close the result relation if necessary, but hold lock on it
-	 * until xact commit.  NB: must not do this till after ExecEndNode(),
-	 * see nodeAppend.c ...
+	 * close the result relation(s) if any, but hold locks
+	 * until xact commit.
 	 */
-	resultRelationInfo = estate->es_result_relation_info;
-	if (resultRelationInfo != NULL)
+	resultRelInfo = estate->es_result_relations;
+	for (i = estate->es_num_result_relations; i > 0; i--)
 	{
-		heap_close(resultRelationInfo->ri_RelationDesc, NoLock);
-		/* close indices on the result relation, too */
-		ExecCloseIndices(resultRelationInfo);
+		/* Close indices and then the relation itself */
+		ExecCloseIndices(resultRelInfo);
+		heap_close(resultRelInfo->ri_RelationDesc, NoLock);
+		resultRelInfo++;
 	}
 
 	/*
@@ -1116,7 +1190,7 @@ ExecAppend(TupleTableSlot *slot,
 		   EState *estate)
 {
 	HeapTuple	tuple;
-	RelationInfo *resultRelationInfo;
+	ResultRelInfo *resultRelInfo;
 	Relation	resultRelationDesc;
 	int			numIndices;
 	Oid			newId;
@@ -1127,10 +1201,10 @@ ExecAppend(TupleTableSlot *slot,
 	tuple = slot->val;
 
 	/*
-	 * get information on the result relation
+	 * get information on the (current) result relation
 	 */
-	resultRelationInfo = estate->es_result_relation_info;
-	resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+	resultRelInfo = estate->es_result_relation_info;
+	resultRelationDesc = resultRelInfo->ri_RelationDesc;
 
 	/* BEFORE ROW INSERT Triggers */
 	if (resultRelationDesc->trigdesc &&
@@ -1154,9 +1228,8 @@ ExecAppend(TupleTableSlot *slot,
 	/*
 	 * Check the constraints of the tuple
 	 */
-
 	if (resultRelationDesc->rd_att->constr)
-		ExecConstraints("ExecAppend", resultRelationDesc, slot, estate);
+		ExecConstraints("ExecAppend", resultRelInfo, slot, estate);
 
 	/*
 	 * insert the tuple
@@ -1174,7 +1247,7 @@ ExecAppend(TupleTableSlot *slot,
 	 * the tupleid of the new tuple is placed in the new tuple's t_ctid
 	 * field.
 	 */
-	numIndices = resultRelationInfo->ri_NumIndices;
+	numIndices = resultRelInfo->ri_NumIndices;
 	if (numIndices > 0)
 		ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
 
@@ -1195,16 +1268,16 @@ ExecDelete(TupleTableSlot *slot,
 		   ItemPointer tupleid,
 		   EState *estate)
 {
-	RelationInfo *resultRelationInfo;
+	ResultRelInfo *resultRelInfo;
 	Relation	resultRelationDesc;
 	ItemPointerData ctid;
 	int			result;
 
 	/*
-	 * get the result relation information
+	 * get information on the (current) result relation
 	 */
-	resultRelationInfo = estate->es_result_relation_info;
-	resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+	resultRelInfo = estate->es_result_relation_info;
+	resultRelationDesc = resultRelInfo->ri_RelationDesc;
 
 	/* BEFORE ROW DELETE Triggers */
 	if (resultRelationDesc->trigdesc &&
@@ -1237,7 +1310,7 @@ ldelete:;
 			else if (!(ItemPointerEquals(tupleid, &ctid)))
 			{
 				TupleTableSlot *epqslot = EvalPlanQual(estate,
-						  resultRelationInfo->ri_RangeTableIndex, &ctid);
+						  resultRelInfo->ri_RangeTableIndex, &ctid);
 
 				if (!TupIsNull(epqslot))
 				{
@@ -1287,7 +1360,7 @@ ExecReplace(TupleTableSlot *slot,
 			EState *estate)
 {
 	HeapTuple	tuple;
-	RelationInfo *resultRelationInfo;
+	ResultRelInfo *resultRelInfo;
 	Relation	resultRelationDesc;
 	ItemPointerData ctid;
 	int			result;
@@ -1308,10 +1381,10 @@ ExecReplace(TupleTableSlot *slot,
 	tuple = slot->val;
 
 	/*
-	 * get the result relation information
+	 * get information on the (current) result relation
 	 */
-	resultRelationInfo = estate->es_result_relation_info;
-	resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+	resultRelInfo = estate->es_result_relation_info;
+	resultRelationDesc = resultRelInfo->ri_RelationDesc;
 
 	/* BEFORE ROW UPDATE Triggers */
 	if (resultRelationDesc->trigdesc &&
@@ -1335,9 +1408,8 @@ ExecReplace(TupleTableSlot *slot,
 	/*
 	 * Check the constraints of the tuple
 	 */
-
 	if (resultRelationDesc->rd_att->constr)
-		ExecConstraints("ExecReplace", resultRelationDesc, slot, estate);
+		ExecConstraints("ExecReplace", resultRelInfo, slot, estate);
 
 	/*
 	 * replace the heap tuple
@@ -1358,7 +1430,7 @@ lreplace:;
 			else if (!(ItemPointerEquals(tupleid, &ctid)))
 			{
 				TupleTableSlot *epqslot = EvalPlanQual(estate,
-						  resultRelationInfo->ri_RangeTableIndex, &ctid);
+						  resultRelInfo->ri_RangeTableIndex, &ctid);
 
 				if (!TupIsNull(epqslot))
 				{
@@ -1396,7 +1468,7 @@ lreplace:;
 	 * there.
 	 */
 
-	numIndices = resultRelationInfo->ri_NumIndices;
+	numIndices = resultRelInfo->ri_NumIndices;
 	if (numIndices > 0)
 		ExecInsertIndexTuples(slot, &(tuple->t_self), estate, true);
 
@@ -1406,8 +1478,10 @@ lreplace:;
 }
 
 static char *
-ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
+ExecRelCheck(ResultRelInfo *resultRelInfo,
+			 TupleTableSlot *slot, EState *estate)
 {
+	Relation	rel = resultRelInfo->ri_RelationDesc;
 	int			ncheck = rel->rd_att->constr->num_check;
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
@@ -1415,6 +1489,24 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
 	List	   *qual;
 	int			i;
 
+	/*
+	 * If first time through for this result relation, build expression
+	 * nodetrees for rel's constraint expressions.  Keep them in the
+	 * per-query memory context so they'll survive throughout the query.
+	 */
+	if (resultRelInfo->ri_ConstraintExprs == NULL)
+	{
+		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
+		resultRelInfo->ri_ConstraintExprs =
+			(List **) palloc(ncheck * sizeof(List *));
+		for (i = 0; i < ncheck; i++)
+		{
+			qual = (List *) stringToNode(check[i].ccbin);
+			resultRelInfo->ri_ConstraintExprs[i] = qual;
+		}
+		MemoryContextSwitchTo(oldContext);
+	}
+
 	/*
 	 * We will use the EState's per-tuple context for evaluating constraint
 	 * expressions.  Create it if it's not already there; if it is, reset it
@@ -1431,58 +1523,13 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
 	else
 		ResetExprContext(econtext);
 
-	/*
-	 * If first time through for current result relation, set up econtext's
-	 * range table to refer to result rel, and build expression nodetrees
-	 * for rel's constraint expressions.  All this stuff is kept in the
-	 * per-query memory context so it will still be here next time through.
-	 *
-	 * NOTE: if there are multiple result relations (eg, due to inheritance)
-	 * then we leak storage for prior rel's expressions and rangetable.
-	 * This should not be a big problem as long as result rels are processed
-	 * sequentially within a command.
-	 */
-	if (econtext->ecxt_range_table == NIL ||
-		getrelid(1, econtext->ecxt_range_table) != RelationGetRelid(rel))
-	{
-		RangeTblEntry *rte;
-
-		/*
-		 * Make sure expressions, etc are placed in appropriate context.
-		 */
-		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
-
-		rte = makeNode(RangeTblEntry);
-
-		rte->relname = RelationGetRelationName(rel);
-		rte->relid = RelationGetRelid(rel);
-		rte->eref = makeNode(Attr);
-		rte->eref->relname = rte->relname;
-		/* other fields won't be used, leave them zero */
-
-		/* Set up single-entry range table */
-		econtext->ecxt_range_table = makeList1(rte);
-
-		estate->es_result_relation_constraints =
-			(List **) palloc(ncheck * sizeof(List *));
-
-		for (i = 0; i < ncheck; i++)
-		{
-			qual = (List *) stringToNode(check[i].ccbin);
-			estate->es_result_relation_constraints[i] = qual;
-		}
-
-		/* Done with building long-lived items */
-		MemoryContextSwitchTo(oldContext);
-	}
-
 	/* Arrange for econtext's scan tuple to be the tuple under test */
 	econtext->ecxt_scantuple = slot;
 
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = estate->es_result_relation_constraints[i];
+		qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL92 specifies that a NULL result from a constraint
@@ -1498,9 +1545,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
 }
 
 void
-ExecConstraints(char *caller, Relation rel,
+ExecConstraints(char *caller, ResultRelInfo *resultRelInfo,
 				TupleTableSlot *slot, EState *estate)
 {
+	Relation	rel = resultRelInfo->ri_RelationDesc;
 	HeapTuple	tuple = slot->val;
 	TupleConstr *constr = rel->rd_att->constr;
 
@@ -1524,7 +1572,7 @@ ExecConstraints(char *caller, Relation rel,
 	{
 		char	   *failed;
 
-		if ((failed = ExecRelCheck(rel, slot, estate)) != NULL)
+		if ((failed = ExecRelCheck(resultRelInfo, slot, estate)) != NULL)
 			elog(ERROR, "%s: rejected due to CHECK constraint %s",
 				 caller, failed);
 	}
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 622ea2ef82c..beeff0db331 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.80 2000/08/24 23:34:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.81 2000/11/12 00:36:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -341,7 +341,6 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
 		tempSlot->ttc_descIsNew = true;
 		tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL;
 		tempSlot->ttc_buffer = InvalidBuffer;
-		tempSlot->ttc_whichplan = -1;
 
 		tup = heap_copytuple(heapTuple);
 		td = CreateTupleDescCopy(tuple_type);
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 408716abf83..65eef4141f4 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.42 2000/10/26 21:35:15 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.43 2000/11/12 00:36:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -303,7 +303,6 @@ ExecAllocTableSlot(TupleTable table)
 	slot->ttc_descIsNew = true;
 	slot->ttc_tupleDescriptor = (TupleDesc) NULL;
 	slot->ttc_buffer = InvalidBuffer;
-	slot->ttc_whichplan = -1;
 
 	return slot;
 }
@@ -675,20 +674,11 @@ NodeGetResultTupleSlot(Plan *node)
 
 		case T_Append:
 			{
-				Append	   *n = (Append *) node;
-				AppendState *appendstate;
-				List	   *appendplans;
-				int			whichplan;
-				Plan	   *subplan;
+				AppendState *appendstate = ((Append *) node)->appendstate;
 
-				appendstate = n->appendstate;
-				appendplans = n->appendplans;
-				whichplan = appendstate->as_whichplan;
-
-				subplan = (Plan *) nth(whichplan, appendplans);
-				slot = NodeGetResultTupleSlot(subplan);
-				break;
+				slot = appendstate->cstate.cs_ResultTupleSlot;
 			}
+			break;
 
 		case T_IndexScan:
 			{
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 4eff4a07bb2..ecdda594188 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.67 2000/10/05 19:48:25 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.68 2000/11/12 00:36:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -172,7 +172,6 @@ ExecAssignExprContext(EState *estate, CommonState *commonstate)
 	econtext->ecxt_param_list_info = estate->es_param_list_info;
 	econtext->ecxt_aggvalues = NULL;
 	econtext->ecxt_aggnulls = NULL;
-	econtext->ecxt_range_table = estate->es_range_table;
 
 	commonstate->cs_ExprContext = econtext;
 }
@@ -215,7 +214,6 @@ MakeExprContext(TupleTableSlot *slot,
 	econtext->ecxt_param_list_info = NULL;
 	econtext->ecxt_aggvalues = NULL;
 	econtext->ecxt_aggnulls = NULL;
-	econtext->ecxt_range_table = NIL;
 
 	return econtext;
 }
@@ -649,10 +647,10 @@ QueryDescGetTypeInfo(QueryDesc *queryDesc)
  *		ExecOpenIndices
  *
  *		Find the indices associated with a result relation, open them,
- *		and save information about them in the result RelationInfo.
+ *		and save information about them in the result ResultRelInfo.
  *
  *		At entry, caller has already opened and locked
- *		resultRelationInfo->ri_RelationDesc.
+ *		resultRelInfo->ri_RelationDesc.
  *
  *		This used to be horribly ugly code, and slow too because it
  *		did a sequential scan of pg_index.  Now we rely on the relcache
@@ -662,9 +660,9 @@ QueryDescGetTypeInfo(QueryDesc *queryDesc)
  * ----------------------------------------------------------------
  */
 void
-ExecOpenIndices(RelationInfo *resultRelationInfo)
+ExecOpenIndices(ResultRelInfo *resultRelInfo)
 {
-	Relation	resultRelation = resultRelationInfo->ri_RelationDesc;
+	Relation	resultRelation = resultRelInfo->ri_RelationDesc;
 	List	   *indexoidlist,
 			   *indexoidscan;
 	int			len,
@@ -672,7 +670,7 @@ ExecOpenIndices(RelationInfo *resultRelationInfo)
 	RelationPtr relationDescs;
 	IndexInfo **indexInfoArray;
 
-	resultRelationInfo->ri_NumIndices = 0;
+	resultRelInfo->ri_NumIndices = 0;
 
 	/* checks for disabled indexes */
 	if (! RelationGetForm(resultRelation)->relhasindex)
@@ -697,9 +695,9 @@ ExecOpenIndices(RelationInfo *resultRelationInfo)
 	relationDescs = (RelationPtr) palloc(len * sizeof(Relation));
 	indexInfoArray = (IndexInfo **) palloc(len * sizeof(IndexInfo *));
 
-	resultRelationInfo->ri_NumIndices = len;
-	resultRelationInfo->ri_IndexRelationDescs = relationDescs;
-	resultRelationInfo->ri_IndexRelationInfo = indexInfoArray;
+	resultRelInfo->ri_NumIndices = len;
+	resultRelInfo->ri_IndexRelationDescs = relationDescs;
+	resultRelInfo->ri_IndexRelationInfo = indexInfoArray;
 
 	/* ----------------
 	 *	 For each index, open the index relation and save pg_index info.
@@ -765,18 +763,18 @@ ExecOpenIndices(RelationInfo *resultRelationInfo)
 /* ----------------------------------------------------------------
  *		ExecCloseIndices
  *
- *		Close the index relations stored in resultRelationInfo
+ *		Close the index relations stored in resultRelInfo
  * ----------------------------------------------------------------
  */
 void
-ExecCloseIndices(RelationInfo *resultRelationInfo)
+ExecCloseIndices(ResultRelInfo *resultRelInfo)
 {
 	int			i;
 	int			numIndices;
 	RelationPtr relationDescs;
 
-	numIndices = resultRelationInfo->ri_NumIndices;
-	relationDescs = resultRelationInfo->ri_IndexRelationDescs;
+	numIndices = resultRelInfo->ri_NumIndices;
+	relationDescs = resultRelInfo->ri_IndexRelationDescs;
 
 	for (i = 0; i < numIndices; i++)
 	{
@@ -817,7 +815,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 					  bool is_update)
 {
 	HeapTuple	heapTuple;
-	RelationInfo *resultRelationInfo;
+	ResultRelInfo *resultRelInfo;
 	int			i;
 	int			numIndices;
 	RelationPtr relationDescs;
@@ -833,11 +831,11 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 	/*
 	 * Get information from the result relation info structure.
 	 */
-	resultRelationInfo = estate->es_result_relation_info;
-	numIndices = resultRelationInfo->ri_NumIndices;
-	relationDescs = resultRelationInfo->ri_IndexRelationDescs;
-	indexInfoArray = resultRelationInfo->ri_IndexRelationInfo;
-	heapRelation = resultRelationInfo->ri_RelationDesc;
+	resultRelInfo = estate->es_result_relation_info;
+	numIndices = resultRelInfo->ri_NumIndices;
+	relationDescs = resultRelInfo->ri_IndexRelationDescs;
+	indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
+	heapRelation = resultRelInfo->ri_RelationDesc;
 	heapDescriptor = RelationGetDescr(heapRelation);
 
 	/*
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index e5a5e55ef8d..ca9c48e294d 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.39 2000/10/26 21:35:15 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.40 2000/11/12 00:36:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -253,7 +253,6 @@ init_sql_fcache(FmgrInfo *finfo)
 		slot->ttc_descIsNew = true;
 		slot->ttc_tupleDescriptor = (TupleDesc) NULL;
 		slot->ttc_buffer = InvalidBuffer;
-		slot->ttc_whichplan = -1;
 
 		fcache->funcSlot = slot;
 	}
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 81eeb1e8b0b..e47ec5756cb 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.37 2000/11/09 18:12:53 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.38 2000/11/12 00:36:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,8 +36,8 @@
  *			  nil	nil		 ...	...    ...
  *								 subplans
  *
- *		Append nodes are currently used for unions, and to support inheritance
- *		queries, where several relations need to be scanned.
+ *		Append nodes are currently used for unions, and to support
+ *		inheritance queries, where several relations need to be scanned.
  *		For example, in our standard person/student/employee/student-emp
  *		example, where student and employee inherit from person
  *		and student-emp inherits from student and employee, the
@@ -54,8 +54,8 @@
  *							  |		  |		   |		|
  *							person employee student student-emp
  */
-#include "postgres.h"
 
+#include "postgres.h"
 
 #include "access/heapam.h"
 #include "executor/execdebug.h"
@@ -78,12 +78,8 @@ exec_append_initialize_next(Append *node)
 {
 	EState	   *estate;
 	AppendState *appendstate;
-	TupleTableSlot *result_slot;
-	List	   *rangeTable;
 	int			whichplan;
 	int			nplans;
-	List	   *inheritrtable;
-	RangeTblEntry *rtentry;
 
 	/* ----------------
 	 *	get information from the append node
@@ -91,12 +87,8 @@ exec_append_initialize_next(Append *node)
 	 */
 	estate = node->plan.state;
 	appendstate = node->appendstate;
-	result_slot = appendstate->cstate.cs_ResultTupleSlot;
-	rangeTable = estate->es_range_table;
-
 	whichplan = appendstate->as_whichplan;
 	nplans = appendstate->as_nplans;
-	inheritrtable = node->inheritrtable;
 
 	if (whichplan < 0)
 	{
@@ -110,7 +102,6 @@ exec_append_initialize_next(Append *node)
 		 */
 		appendstate->as_whichplan = 0;
 		return FALSE;
-
 	}
 	else if (whichplan >= nplans)
 	{
@@ -121,37 +112,25 @@ exec_append_initialize_next(Append *node)
 		 */
 		appendstate->as_whichplan = nplans - 1;
 		return FALSE;
-
 	}
 	else
 	{
 		/* ----------------
 		 *		initialize the scan
-		 *		(and update the range table appropriately)
 		 *
-		 *		(doesn't this leave the range table hosed for anybody upstream
-		 *		 of the Append node??? - jolly )
+		 * If we are controlling the target relation, select the proper
+		 * active ResultRelInfo and junk filter for this target.
 		 * ----------------
 		 */
-		if (node->inheritrelid > 0)
+		if (node->isTarget)
 		{
-			rtentry = nth(whichplan, inheritrtable);
-			Assert(rtentry != NULL);
-			rt_store(node->inheritrelid, rangeTable, rtentry);
+			Assert(whichplan < estate->es_num_result_relations);
+			estate->es_result_relation_info =
+				estate->es_result_relations + whichplan;
+			estate->es_junkFilter =
+				estate->es_result_relation_info->ri_junkFilter;
 		}
 
-		if (appendstate->as_junkFilter_list)
-		{
-			estate->es_junkFilter = (JunkFilter *) nth(whichplan,
-										appendstate->as_junkFilter_list);
-		}
-		if (appendstate->as_result_relation_info_list)
-		{
-			estate->es_result_relation_info = (RelationInfo *) nth(whichplan,
-							  appendstate->as_result_relation_info_list);
-		}
-		result_slot->ttc_whichplan = whichplan;
-
 		return TRUE;
 	}
 }
@@ -176,14 +155,10 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent)
 {
 	AppendState *appendstate;
 	int			nplans;
-	List	   *inheritrtable;
 	List	   *appendplans;
 	bool	   *initialized;
 	int			i;
 	Plan	   *initNode;
-	List	   *junkList;
-	RelationInfo *es_rri = estate->es_result_relation_info;
-	bool		inherited_result_rel = false;
 
 	CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
 
@@ -196,7 +171,6 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent)
 
 	appendplans = node->appendplans;
 	nplans = length(appendplans);
-	inheritrtable = node->inheritrtable;
 
 	initialized = (bool *) palloc(nplans * sizeof(bool));
 	MemSet(initialized, 0, nplans * sizeof(bool));
@@ -228,119 +202,25 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent)
 	 */
 	ExecInitResultTupleSlot(estate, &appendstate->cstate);
 
-	/*
-	 * If the inherits rtentry is the result relation, we have to make a
-	 * result relation info list for all inheritors so we can update their
-	 * indices and put the result tuples in the right place etc.
-	 *
-	 * e.g. replace p (age = p.age + 1) from p in person*
-	 */
-	if ((es_rri != (RelationInfo *) NULL) &&
-		(node->inheritrelid == es_rri->ri_RangeTableIndex))
-	{
-		List	   *resultList = NIL;
-		Oid			initial_reloid = RelationGetRelid(es_rri->ri_RelationDesc);
-		List	   *rtentryP;
-
-		inherited_result_rel = true;
-
-		foreach(rtentryP, inheritrtable)
-		{
-			RangeTblEntry *rtentry = lfirst(rtentryP);
-			Oid			reloid = rtentry->relid;
-			RelationInfo *rri;
-
-			/*
-			 * We must recycle the RelationInfo already opened by InitPlan()
-			 * for the parent rel, else we will leak the associated relcache
-			 * refcount. 
-			 */
-			if (reloid == initial_reloid)
-			{
-				Assert(es_rri != NULL);	/* check we didn't use it already */
-				rri = es_rri;
-				es_rri = NULL;
-			}
-			else
-			{
-				rri = makeNode(RelationInfo);
-				rri->ri_RangeTableIndex = node->inheritrelid;
-				rri->ri_RelationDesc = heap_open(reloid, RowExclusiveLock);
-				rri->ri_NumIndices = 0;
-				rri->ri_IndexRelationDescs = NULL;	/* index descs */
-				rri->ri_IndexRelationInfo = NULL;	/* index key info */
-
-				/*
-				 * XXX if the operation is a DELETE then we need not open
-				 * indices, but how to tell that here?
-				 */
-				if (rri->ri_RelationDesc->rd_rel->relhasindex)
-					ExecOpenIndices(rri);
-			}
-
-			/*
-			 * NB: the as_result_relation_info_list must be in the same
-			 * order as the rtentry list otherwise update or delete on
-			 * inheritance hierarchies won't work.
-			 */
-			resultList = lappend(resultList, rri);
-		}
-
-		appendstate->as_result_relation_info_list = resultList;
-		/* Check that we recycled InitPlan()'s RelationInfo */
-		Assert(es_rri == NULL);
-		/* Just for paranoia's sake, clear link until we set it properly */
-		estate->es_result_relation_info = NULL;
-	}
-
 	/* ----------------
 	 *	call ExecInitNode on each of the plans in our list
 	 *	and save the results into the array "initialized"
 	 * ----------------
 	 */
-	junkList = NIL;
-
 	for (i = 0; i < nplans; i++)
 	{
-		/* ----------------
-		 *	NOTE: we first modify range table in
-		 *		  exec_append_initialize_next() and
-		 *		  then initialize the subnode,
-		 *		  since it may use the range table.
-		 * ----------------
-		 */
 		appendstate->as_whichplan = i;
 		exec_append_initialize_next(node);
 
 		initNode = (Plan *) nth(i, appendplans);
 		initialized[i] = ExecInitNode(initNode, estate, (Plan *) node);
-
-		/* ---------------
-		 *	Each targetlist in the subplan may need its own junk filter
-		 *
-		 *	This is true only when the reln being replaced/deleted is
-		 *	the one that we're looking at the subclasses of
-		 * ---------------
-		 */
-		if (inherited_result_rel)
-		{
-			JunkFilter *j = ExecInitJunkFilter(initNode->targetlist,
-											   ExecGetTupType(initNode));
-
-			junkList = lappend(junkList, j);
-		}
-
 	}
-	appendstate->as_junkFilter_list = junkList;
-	if (junkList != NIL)
-		estate->es_junkFilter = (JunkFilter *) lfirst(junkList);
 
 	/* ----------------
-	 *	initialize the return type from the appropriate subplan.
+	 *	initialize tuple type
 	 * ----------------
 	 */
-	initNode = (Plan *) nth(0, appendplans);
-	ExecAssignResultType(&appendstate->cstate, ExecGetTupType(initNode));
+	ExecAssignResultTypeFromTL((Plan *) node, &appendstate->cstate);
 	appendstate->cstate.cs_ProjInfo = NULL;
 
 	/* ----------------
@@ -357,10 +237,9 @@ int
 ExecCountSlotsAppend(Append *node)
 {
 	List	   *plan;
-	List	   *appendplans = node->appendplans;
 	int			nSlots = 0;
 
-	foreach(plan, appendplans)
+	foreach(plan, node->appendplans)
 		nSlots += ExecCountSlotsNode((Plan *) lfirst(plan));
 	return nSlots + APPEND_NSLOTS;
 }
@@ -430,16 +309,14 @@ ExecProcAppend(Append *node)
 		 *	direction and try processing again (recursively)
 		 * ----------------
 		 */
-		whichplan = appendstate->as_whichplan;
-
 		if (ScanDirectionIsForward(direction))
-			appendstate->as_whichplan = whichplan + 1;
+			appendstate->as_whichplan++;
 		else
-			appendstate->as_whichplan = whichplan - 1;
+			appendstate->as_whichplan--;
 
 		/* ----------------
 		 *	return something from next node or an empty slot
-		 *	all of our subplans have been exhausted.
+		 *	if all of our subplans have been exhausted.
 		 * ----------------
 		 */
 		if (exec_append_initialize_next(node))
@@ -469,7 +346,6 @@ ExecEndAppend(Append *node)
 	List	   *appendplans;
 	bool	   *initialized;
 	int			i;
-	List	   *resultRelationInfoList;
 
 	/* ----------------
 	 *	get information from the node
@@ -490,40 +366,8 @@ ExecEndAppend(Append *node)
 		if (initialized[i])
 			ExecEndNode((Plan *) nth(i, appendplans), (Plan *) node);
 	}
-
-	/* ----------------
-	 *	close out the different result relations
-	 *
-	 *	NB: this must agree with what EndPlan() does to close a result rel
-	 * ----------------
-	 */
-	resultRelationInfoList = appendstate->as_result_relation_info_list;
-	while (resultRelationInfoList != NIL)
-	{
-		RelationInfo *resultRelationInfo;
-
-		resultRelationInfo = (RelationInfo *) lfirst(resultRelationInfoList);
-
-		heap_close(resultRelationInfo->ri_RelationDesc, NoLock);
-		/* close indices on the result relation, too */
-		ExecCloseIndices(resultRelationInfo);
-
-		/*
-		 * estate may (or may not) be pointing at one of my result relations.
-		 * If so, make sure EndPlan() doesn't try to close it again!
-		 */
-		if (estate->es_result_relation_info == resultRelationInfo)
-			estate->es_result_relation_info = NULL;
-
-		pfree(resultRelationInfo);
-		resultRelationInfoList = lnext(resultRelationInfoList);
-	}
-	appendstate->as_result_relation_info_list = NIL;
-
-	/*
-	 * XXX should free appendstate->as_junkfilter_list here
-	 */
 }
+
 void
 ExecReScanAppend(Append *node, ExprContext *exprCtxt, Plan *parent)
 {
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index b953dcd3697..56ba1375feb 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.24 2000/07/12 02:37:04 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.25 2000/11/12 00:36:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -156,7 +156,6 @@ InitScanRelation(SeqScan *node, EState *estate,
 	ScanDirection direction;
 	Relation	currentRelation;
 	HeapScanDesc currentScanDesc;
-	RelationInfo *resultRelationInfo;
 
 	/* ----------------
 	 * get the relation object id from the relid'th entry
@@ -169,7 +168,6 @@ InitScanRelation(SeqScan *node, EState *estate,
 	rtentry = rt_fetch(relid, rangeTable);
 	reloid = rtentry->relid;
 	direction = estate->es_direction;
-	resultRelationInfo = estate->es_result_relation_info;
 
 	ExecOpenScanR(reloid,		/* relation */
 				  0,			/* nkeys */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 93866787f37..3490ef80624 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.130 2000/11/05 22:50:19 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.131 2000/11/12 00:36:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -176,8 +176,7 @@ _copyAppend(Append *from)
 	 * ----------------
 	 */
 	Node_Copy(from, newnode, appendplans);
-	newnode->inheritrelid = from->inheritrelid;
-	Node_Copy(from, newnode, inheritrtable);
+	newnode->isTarget = from->isTarget;
 
 	return newnode;
 }
@@ -1275,6 +1274,30 @@ _copyTidPath(TidPath *from)
 	return newnode;
 }
 
+/* ----------------
+ *				_copyAppendPath
+ * ----------------
+ */
+static AppendPath *
+_copyAppendPath(AppendPath *from)
+{
+	AppendPath    *newnode = makeNode(AppendPath);
+
+	/* ----------------
+	 *	copy the node superclass fields
+	 * ----------------
+	 */
+	CopyPathFields((Path *) from, (Path *) newnode);
+
+	/* ----------------
+	 *	copy remainder of node
+	 * ----------------
+	 */
+	Node_Copy(from, newnode, subpaths);
+
+	return newnode;
+}
+
 /* ----------------
  *		CopyJoinPathFields
  *
@@ -1767,6 +1790,8 @@ _copyQuery(Query *from)
 
 	Node_Copy(from, newnode, setOperations);
 
+	newnode->resultRelations = listCopy(from->resultRelations);
+
 	/*
 	 * We do not copy the planner internal fields: base_rel_list,
 	 * join_rel_list, equi_key_list, query_pathkeys. Not entirely clear if
@@ -2677,6 +2702,9 @@ copyObject(void *from)
 		case T_TidPath:
 			retval = _copyTidPath(from);
 			break;
+		case T_AppendPath:
+			retval = _copyAppendPath(from);
+			break;
 		case T_NestPath:
 			retval = _copyNestPath(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 7ed95db9679..8fb81a45c0b 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -20,7 +20,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.80 2000/11/05 22:50:19 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.81 2000/11/12 00:36:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -435,6 +435,16 @@ _equalTidPath(TidPath *a, TidPath *b)
 	return true;
 }
 
+static bool
+_equalAppendPath(AppendPath *a, AppendPath *b)
+{
+	if (!_equalPath((Path *) a, (Path *) b))
+		return false;
+	if (!equal(a->subpaths, b->subpaths))
+		return false;
+	return true;
+}
+
 static bool
 _equalJoinPath(JoinPath *a, JoinPath *b)
 {
@@ -555,28 +565,6 @@ _equalStream(Stream *a, Stream *b)
 	return equal(a->downstream, b->downstream);
 }
 
-/*
- *	Stuff from execnodes.h
- */
-
-/*
- *	EState is a subclass of Node.
- */
-static bool
-_equalEState(EState *a, EState *b)
-{
-	if (a->es_direction != b->es_direction)
-		return false;
-
-	if (!equal(a->es_range_table, b->es_range_table))
-		return false;
-
-	if (a->es_result_relation_info != b->es_result_relation_info)
-		return false;
-
-	return true;
-}
-
 /*
  * Stuff from parsenodes.h
  */
@@ -624,6 +612,8 @@ _equalQuery(Query *a, Query *b)
 		return false;
 	if (!equal(a->setOperations, b->setOperations))
 		return false;
+	if (!equali(a->resultRelations, b->resultRelations))
+		return false;
 
 	/*
 	 * We do not check the internal-to-the-planner fields: base_rel_list,
@@ -1851,14 +1841,13 @@ equal(void *a, void *b)
 		case T_TidPath:
 			retval = _equalTidPath(a, b);
 			break;
+		case T_AppendPath:
+			retval = _equalAppendPath(a, b);
+			break;
 		case T_IndexOptInfo:
 			retval = _equalIndexOptInfo(a, b);
 			break;
 
-		case T_EState:
-			retval = _equalEState(a, b);
-			break;
-
 		case T_List:
 			{
 				List	   *la = (List *) a;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 58ab6896889..d7137f10ace 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.131 2000/10/31 13:59:52 petere Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.132 2000/11/12 00:36:57 tgl Exp $
  *
  * NOTES
  *	  Every (plan) node in POSTGRES has an associated "out" routine which
@@ -296,6 +296,9 @@ _outQuery(StringInfo str, Query *node)
 
 	appendStringInfo(str, " :setOperations ");
 	_outNode(str, node->setOperations);
+
+	appendStringInfo(str, " :resultRelations ");
+	_outIntList(str, node->resultRelations);
 }
 
 static void
@@ -327,17 +330,18 @@ _outSetOperationStmt(StringInfo str, SetOperationStmt *node)
 
 /*
  * print the basic stuff of all nodes that inherit from Plan
+ *
+ * NOTE: we deliberately omit the execution state (EState)
  */
 static void
 _outPlanInfo(StringInfo str, Plan *node)
 {
 	appendStringInfo(str,
-					 ":startup_cost %.2f :total_cost %.2f :rows %.0f :width %d :state %s :qptargetlist ",
+					 ":startup_cost %.2f :total_cost %.2f :rows %.0f :width %d :qptargetlist ",
 					 node->startup_cost,
 					 node->total_cost,
 					 node->plan_rows,
-					 node->plan_width,
-					 node->state ? "not-NULL" : "<>");
+					 node->plan_width);
 	_outNode(str, node->targetlist);
 
 	appendStringInfo(str, " :qpqual ");
@@ -394,9 +398,8 @@ _outAppend(StringInfo str, Append *node)
 	appendStringInfo(str, " :appendplans ");
 	_outNode(str, node->appendplans);
 
-	appendStringInfo(str, " :inheritrelid %u :inheritrtable ",
-					 node->inheritrelid);
-	_outNode(str, node->inheritrtable);
+	appendStringInfo(str, " :isTarget %s ",
+					 node->isTarget ? "true" : "false");
 }
 
 /*
@@ -945,25 +948,6 @@ _outJoinExpr(StringInfo str, JoinExpr *node)
 	_outNode(str, node->colvars);
 }
 
-/*
- *	Stuff from execnodes.h
- */
-
-/*
- *	EState is a subclass of Node.
- */
-static void
-_outEState(StringInfo str, EState *node)
-{
-	appendStringInfo(str,
-					 " ESTATE :direction %d :range_table ",
-					 node->es_direction);
-	_outNode(str, node->es_range_table);
-
-	appendStringInfo(str, " :result_relation_info @ 0x%x ",
-					 (int) (node->es_result_relation_info));
-}
-
 /*
  *	Stuff from relation.h
  */
@@ -1107,10 +1091,27 @@ _outTidPath(StringInfo str, TidPath *node)
 	appendStringInfo(str, " :tideval ");
 	_outNode(str, node->tideval);
 
-	appendStringInfo(str, " :un joined_relids ");
+	appendStringInfo(str, " :unjoined_relids ");
 	_outIntList(str, node->unjoined_relids);
 }
 
+/*
+ *	AppendPath is a subclass of Path.
+ */
+static void
+_outAppendPath(StringInfo str, AppendPath *node)
+{
+	appendStringInfo(str,
+					 " APPENDPATH :pathtype %d :startup_cost %.2f :total_cost %.2f :pathkeys ",
+					 node->path.pathtype,
+					 node->path.startup_cost,
+					 node->path.total_cost);
+	_outNode(str, node->path.pathkeys);
+
+	appendStringInfo(str, " :subpaths ");
+	_outNode(str, node->subpaths);
+}
+
 /*
  *	NestPath is a subclass of Path
  */
@@ -1632,9 +1633,6 @@ _outNode(StringInfo str, void *obj)
 			case T_JoinExpr:
 				_outJoinExpr(str, obj);
 				break;
-			case T_EState:
-				_outEState(str, obj);
-				break;
 			case T_RelOptInfo:
 				_outRelOptInfo(str, obj);
 				break;
@@ -1656,6 +1654,9 @@ _outNode(StringInfo str, void *obj)
 			case T_TidPath:
 				_outTidPath(str, obj);
 				break;
+			case T_AppendPath:
+				_outAppendPath(str, obj);
+				break;
 			case T_NestPath:
 				_outNestPath(str, obj);
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 354ac8d5839..6a6dce48bfb 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.99 2000/10/22 22:14:54 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.100 2000/11/12 00:36:57 tgl Exp $
  *
  * NOTES
  *	  Most of the read functions for plan nodes are tested. (In fact, they
@@ -150,6 +150,9 @@ _readQuery(void)
 	token = lsptok(NULL, &length);		/* skip :setOperations */
 	local_node->setOperations = nodeRead(true);
 
+	token = lsptok(NULL, &length);		/* skip :resultRelations */
+	local_node->resultRelations = toIntList(nodeRead(true));
+
 	return local_node;
 }
 
@@ -260,17 +263,6 @@ _getPlan(Plan *node)
 	token = lsptok(NULL, &length);		/* get the plan_width */
 	node->plan_width = atoi(token);
 
-	token = lsptok(NULL, &length);		/* eat the :state stuff */
-	token = lsptok(NULL, &length);		/* now get the state */
-
-	if (length == 0)
-		node->state = (EState *) NULL;
-	else
-	{							/* Disgusting hack until I figure out what
-								 * to do here */
-		node->state = (EState *) !NULL;
-	}
-
 	token = lsptok(NULL, &length);		/* eat :qptargetlist */
 	node->targetlist = nodeRead(true);
 
@@ -283,6 +275,8 @@ _getPlan(Plan *node)
 	token = lsptok(NULL, &length);		/* eat :righttree */
 	node->righttree = (Plan *) nodeRead(true);
 
+	node->state = (EState *) NULL;		/* never read in */
+
 	return;
 }
 
@@ -348,12 +342,9 @@ _readAppend(void)
 	token = lsptok(NULL, &length);		/* eat :appendplans */
 	local_node->appendplans = nodeRead(true);	/* now read it */
 
-	token = lsptok(NULL, &length);		/* eat :inheritrelid */
-	token = lsptok(NULL, &length);		/* get inheritrelid */
-	local_node->inheritrelid = strtoul(token, NULL, 10);
-
-	token = lsptok(NULL, &length);		/* eat :inheritrtable */
-	local_node->inheritrtable = nodeRead(true); /* now read it */
+	token = lsptok(NULL, &length);		/* eat :isTarget */
+	token = lsptok(NULL, &length);		/* get isTarget */
+	local_node->isTarget = (token[0] == 't') ? true : false;
 
 	return local_node;
 }
@@ -1297,43 +1288,6 @@ _readJoinExpr(void)
 	return local_node;
 }
 
-/*
- *	Stuff from execnodes.h
- */
-
-/* ----------------
- *		_readEState
- *
- *	EState is a subclass of Node.
- * ----------------
- */
-static EState *
-_readEState(void)
-{
-	EState	   *local_node;
-	char	   *token;
-	int			length;
-
-	local_node = makeNode(EState);
-
-	token = lsptok(NULL, &length);		/* get :direction */
-	token = lsptok(NULL, &length);		/* now read it */
-
-	local_node->es_direction = atoi(token);
-
-	token = lsptok(NULL, &length);		/* get :range_table */
-
-	local_node->es_range_table = nodeRead(true);		/* now read it */
-
-	token = lsptok(NULL, &length);		/* get :result_relation_info */
-	token = lsptok(NULL, &length);		/* get @ */
-	token = lsptok(NULL, &length);		/* now read it */
-
-	sscanf(token, "%x", (unsigned int *) &local_node->es_result_relation_info);
-
-	return local_node;
-}
-
 /*
  *	Stuff from relation.h
  */
@@ -1639,6 +1593,42 @@ _readTidPath(void)
 	return local_node;
 }
 
+/* ----------------
+ *		_readAppendPath
+ *
+ *	AppendPath is a subclass of Path.
+ * ----------------
+ */
+static AppendPath *
+_readAppendPath(void)
+{
+	AppendPath *local_node;
+	char	   *token;
+	int			length;
+
+	local_node = makeNode(AppendPath);
+
+	token = lsptok(NULL, &length);		/* get :pathtype */
+	token = lsptok(NULL, &length);		/* now read it */
+	local_node->path.pathtype = atol(token);
+
+	token = lsptok(NULL, &length);		/* get :startup_cost */
+	token = lsptok(NULL, &length);		/* now read it */
+	local_node->path.startup_cost = (Cost) atof(token);
+
+	token = lsptok(NULL, &length);		/* get :total_cost */
+	token = lsptok(NULL, &length);		/* now read it */
+	local_node->path.total_cost = (Cost) atof(token);
+
+	token = lsptok(NULL, &length);		/* get :pathkeys */
+	local_node->path.pathkeys = nodeRead(true); /* now read it */
+
+	token = lsptok(NULL, &length);		/* get :subpaths */
+	local_node->subpaths = nodeRead(true);		/* now read it */
+
+	return local_node;
+}
+
 /* ----------------
  *		_readNestPath
  *
@@ -1984,8 +1974,6 @@ parsePlanString(void)
 		return_value = _readOper();
 	else if (length == 5 && strncmp(token, "PARAM", length) == 0)
 		return_value = _readParam();
-	else if (length == 6 && strncmp(token, "ESTATE", length) == 0)
-		return_value = _readEState();
 	else if (length == 10 && strncmp(token, "RELOPTINFO", length) == 0)
 		return_value = _readRelOptInfo();
 	else if (length == 11 && strncmp(token, "TARGETENTRY", length) == 0)
@@ -1998,6 +1986,8 @@ parsePlanString(void)
 		return_value = _readIndexPath();
 	else if (length == 7 && strncmp(token, "TIDPATH", length) == 0)
 		return_value = _readTidPath();
+	else if (length == 10 && strncmp(token, "APPENDPATH", length) == 0)
+		return_value = _readAppendPath();
 	else if (length == 8 && strncmp(token, "NESTPATH", length) == 0)
 		return_value = _readNestPath();
 	else if (length == 9 && strncmp(token, "MERGEPATH", length) == 0)
diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README
index f0113dfaf50..e60f1457208 100644
--- a/src/backend/optimizer/README
+++ b/src/backend/optimizer/README
@@ -4,11 +4,11 @@ Summary
 These directories take the Query structure returned by the parser, and
 generate a plan used by the executor.  The /plan directory generates the
 actual output plan, the /path code generates all possible ways to join the
-tables, and /prep handles special cases like inheritance.  /util is utility
-stuff.  /geqo is the separate "genetic optimization" planner --- it does
-a semi-random search through the join tree space, rather than exhaustively
-considering all possible join trees.  (But each join considered by /geqo
-is given to /path to create paths for, so we consider all possible
+tables, and /prep handles various preprocessing steps for special cases.
+/util is utility stuff.  /geqo is the separate "genetic optimization" planner
+--- it does a semi-random search through the join tree space, rather than
+exhaustively considering all possible join trees.  (But each join considered
+by /geqo is given to /path to create paths for, so we consider all possible
 implementation paths for each specific join pair even in GEQO mode.)
 
 
@@ -210,10 +210,10 @@ planner()
      thereby reducing the accuracy of selectivity estimates.
  process sublinks
  convert Vars of outer query levels into Params
---union_planner()
-  handle unions and inheritance by mutual recursion with prepunion.c routines
-  preprocess target list
-  handle GROUP BY, HAVING, aggregates, ORDER BY, DISTINCT
+--grouping_planner()
+  preprocess target list for non-SELECT queries
+  handle UNION/INTERSECT/EXCEPT, GROUP BY, HAVING, aggregates,
+	ORDER BY, DISTINCT, LIMIT
 --query_planner()
    pull out constant quals, which can be used to gate execution of the
 	whole plan (if any are found, we make a top-level Result node
@@ -239,11 +239,12 @@ planner()
       Loop back if this wasn't the top join level.
    Back at query_planner:
    put back constant quals and non-simplified target list
- Back at union_planner:
+ Back at grouping_planner:
  do grouping(GROUP)
  do aggregates
  make unique(DISTINCT)
  make sort(ORDER BY)
+ make limit(LIMIT/OFFSET)
 
 
 Optimizer Data Structures
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 7e017a746f1..12fc576612f 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.66 2000/10/05 19:11:28 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.67 2000/11/12 00:36:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,6 +21,7 @@
 #include "optimizer/paths.h"
 #include "optimizer/plancat.h"
 #include "optimizer/planner.h"
+#include "optimizer/prep.h"
 #include "parser/parsetree.h"
 
 
@@ -28,7 +29,12 @@ bool		enable_geqo = true;
 int			geqo_rels = DEFAULT_GEQO_RELS;
 
 
-static void set_base_rel_pathlist(Query *root);
+static void set_base_rel_pathlists(Query *root);
+static void set_plain_rel_pathlist(Query *root, RelOptInfo *rel,
+								   RangeTblEntry *rte);
+static void set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
+									   RangeTblEntry *rte,
+									   List *inheritlist);
 static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
 										 List *initial_rels);
 
@@ -51,7 +57,7 @@ make_one_rel(Query *root)
 	/*
 	 * Generate access paths for the base rels.
 	 */
-	set_base_rel_pathlist(root);
+	set_base_rel_pathlists(root);
 
 	/*
 	 * Generate access paths for the entire join tree.
@@ -69,23 +75,26 @@ make_one_rel(Query *root)
 }
 
 /*
- * set_base_rel_pathlist
+ * set_base_rel_pathlists
  *	  Finds all paths available for scanning each base-relation entry.
  *	  Sequential scan and any available indices are considered.
  *	  Each useful path is attached to its relation's 'pathlist' field.
  */
 static void
-set_base_rel_pathlist(Query *root)
+set_base_rel_pathlists(Query *root)
 {
 	List	   *rellist;
 
 	foreach(rellist, root->base_rel_list)
 	{
 		RelOptInfo *rel = (RelOptInfo *) lfirst(rellist);
+		Index		rti;
 		RangeTblEntry *rte;
+		List	   *inheritlist;
 
 		Assert(length(rel->relids) == 1); /* better be base rel */
-		rte = rt_fetch(lfirsti(rel->relids), root->rtable);
+		rti = lfirsti(rel->relids);
+		rte = rt_fetch(rti, root->rtable);
 
 		if (rel->issubquery)
 		{
@@ -109,47 +118,163 @@ set_base_rel_pathlist(Query *root)
 
 			/* Generate appropriate path */
 			add_path(rel, create_subqueryscan_path(rel));
+
+			/* Select cheapest path (pretty easy in this case...) */
+			set_cheapest(rel);
+		}
+		else if ((inheritlist = expand_inherted_rtentry(root, rti)) != NIL)
+		{
+			/* Relation is root of an inheritance tree, process specially */
+			set_inherited_rel_pathlist(root, rel, rte, inheritlist);
 		}
 		else
 		{
 			/* Plain relation */
-			List	   *indices = find_secondary_indexes(rte->relid);
-
-			/* Mark rel with estimated output rows, width, etc */
-			set_baserel_size_estimates(root, rel);
-
-			/*
-			 * Generate paths and add them to the rel's pathlist.
-			 *
-			 * Note: add_path() will discard any paths that are dominated by
-			 * another available path, keeping only those paths that are
-			 * superior along at least one dimension of cost or sortedness.
-			 */
-
-			/* Consider sequential scan */
-			add_path(rel, create_seqscan_path(rel));
-
-			/* Consider TID scans */
-			create_tidscan_paths(root, rel);
-
-			/* Consider index paths for both simple and OR index clauses */
-			create_index_paths(root, rel, indices,
-							   rel->baserestrictinfo,
-							   rel->joininfo);
-
-			/*
-			 * Note: create_or_index_paths depends on create_index_paths to
-			 * have marked OR restriction clauses with relevant indices; this
-			 * is why it doesn't need to be given the list of indices.
-			 */
-			create_or_index_paths(root, rel, rel->baserestrictinfo);
+			set_plain_rel_pathlist(root, rel, rte);
 		}
-
-		/* Now find the cheapest of the paths for this rel */
-		set_cheapest(rel);
 	}
 }
 
+/*
+ * set_plain_rel_pathlist
+ *	  Build access paths for a plain relation (no subquery, no inheritance)
+ */
+static void
+set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+	List	   *indices = find_secondary_indexes(rte->relid);
+
+	/* Mark rel with estimated output rows, width, etc */
+	set_baserel_size_estimates(root, rel);
+
+	/*
+	 * Generate paths and add them to the rel's pathlist.
+	 *
+	 * Note: add_path() will discard any paths that are dominated by
+	 * another available path, keeping only those paths that are
+	 * superior along at least one dimension of cost or sortedness.
+	 */
+
+	/* Consider sequential scan */
+	add_path(rel, create_seqscan_path(rel));
+
+	/* Consider TID scans */
+	create_tidscan_paths(root, rel);
+
+	/* Consider index paths for both simple and OR index clauses */
+	create_index_paths(root, rel, indices,
+					   rel->baserestrictinfo,
+					   rel->joininfo);
+
+	/*
+	 * Note: create_or_index_paths depends on create_index_paths to
+	 * have marked OR restriction clauses with relevant indices; this
+	 * is why it doesn't need to be given the list of indices.
+	 */
+	create_or_index_paths(root, rel, rel->baserestrictinfo);
+
+	/* Now find the cheapest of the paths for this rel */
+	set_cheapest(rel);
+}
+
+/*
+ * set_inherited_rel_pathlist
+ *	  Build access paths for a inheritance tree rooted at rel
+ *
+ * inheritlist is a list of RT indexes of all tables in the inheritance tree,
+ * including the parent itself.  Note we will not come here unless there's
+ * at least one child in addition to the parent.
+ */
+static void
+set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte,
+						   List *inheritlist)
+{
+	int			parentRTindex = lfirsti(rel->relids);
+	Oid			parentOID = rte->relid;
+	List	   *subpaths = NIL;
+	List	   *il;
+
+	/*
+	 * XXX for now, can't handle inherited expansion of FOR UPDATE;
+	 * can we do better?
+	 */
+	if (intMember(parentRTindex, root->rowMarks))
+		elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries");
+
+	/*
+	 * Recompute size estimates for whole inheritance tree
+	 */
+	rel->rows = 0;
+	rel->width = 0;
+
+	/*
+	 * Generate access paths for each table in the tree (parent AND children),
+	 * and pick the cheapest path for each table.
+	 */
+	foreach(il, inheritlist)
+	{
+		int		childRTindex = lfirsti(il);
+		RangeTblEntry *childrte;
+		Oid		childOID;
+		RelOptInfo *childrel;
+
+		childrte = rt_fetch(childRTindex, root->rtable);
+		childOID = childrte->relid;
+
+		/*
+		 * Make a RelOptInfo for the child so we can do planning.  Do NOT
+		 * attach the RelOptInfo to the query's base_rel_list, however.
+		 *
+		 * NOTE: when childRTindex == parentRTindex, we create a second
+		 * RelOptInfo for the same relation.  This RelOptInfo will represent
+		 * the parent table alone, whereas the original RelOptInfo represents
+		 * the union of the inheritance tree members.
+		 */
+		childrel = make_base_rel(root, childRTindex);
+
+		/*
+		 * Copy the parent's targetlist and restriction quals to the child,
+		 * with attribute-number adjustment if needed.  We don't bother
+		 * to copy the join quals, since we can't do any joining here.
+		 */
+		childrel->targetlist = (List *)
+			adjust_inherited_attrs((Node *) rel->targetlist,
+								   parentRTindex,
+								   parentOID,
+								   childRTindex,
+								   childOID);
+		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.
+		 */
+		set_plain_rel_pathlist(root, childrel, childrte);
+
+		subpaths = lappend(subpaths, childrel->cheapest_total_path);
+
+		/* Also update total size estimates */
+		rel->rows += childrel->rows;
+		if (childrel->width > rel->width)
+			rel->width = childrel->width;
+	}
+
+	/*
+	 * Finally, build Append path and install it as the only access
+	 * path for the parent rel.
+	 */
+	add_path(rel, (Path *) create_append_path(rel, subpaths));
+
+	/* Select cheapest path (pretty easy in this case...) */
+	set_cheapest(rel);
+}
+
+
 /*
  * make_fromexpr_rel
  *	  Build access paths for a FromExpr jointree node.
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 820492bd186..f94d2e4037b 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.26 2000/09/29 18:21:32 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.27 2000/11/12 00:36:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -566,8 +566,8 @@ build_join_pathkeys(List *outer_pathkeys,
  * NB: the result is NOT in canonical form, but must be passed through
  * canonicalize_pathkeys() before it can be used for comparisons or
  * labeling relation sort orders.  (We do things this way because
- * union_planner needs to be able to construct requested pathkeys before
- * the pathkey equivalence sets have been created for the query.)
+ * grouping_planner needs to be able to construct requested pathkeys
+ * before the pathkey equivalence sets have been created for the query.)
  *
  * 'sortclauses' is a list of SortClause or GroupClause nodes
  * 'tlist' is the targetlist to find the referenced tlist entries in
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index a865da61b92..4069ed66e58 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.99 2000/10/26 21:36:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.100 2000/11/12 00:36:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,35 +32,38 @@
 #include "utils/syscache.h"
 
 
-static List *switch_outer(List *clauses);
-static Scan *create_scan_node(Query *root, Path *best_path, List *tlist);
-static Join *create_join_node(Query *root, JoinPath *best_path, List *tlist);
-static SeqScan *create_seqscan_node(Path *best_path, List *tlist,
+static Scan *create_scan_plan(Query *root, Path *best_path);
+static Join *create_join_plan(Query *root, JoinPath *best_path);
+static Append *create_append_plan(Query *root, AppendPath *best_path);
+static SeqScan *create_seqscan_plan(Path *best_path, List *tlist,
 					List *scan_clauses);
-static IndexScan *create_indexscan_node(Query *root, IndexPath *best_path,
+static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path,
 					  List *tlist, List *scan_clauses);
-static TidScan *create_tidscan_node(TidPath *best_path, List *tlist,
+static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist,
 					List *scan_clauses);
-static SubqueryScan *create_subqueryscan_node(Path *best_path,
+static SubqueryScan *create_subqueryscan_plan(Path *best_path,
 					  List *tlist, List *scan_clauses);
-static NestLoop *create_nestloop_node(NestPath *best_path, List *tlist,
+static NestLoop *create_nestloop_plan(NestPath *best_path, List *tlist,
 									  List *joinclauses, List *otherclauses,
-									  Plan *outer_node, List *outer_tlist,
-									  Plan *inner_node, List *inner_tlist);
-static MergeJoin *create_mergejoin_node(MergePath *best_path, List *tlist,
+									  Plan *outer_plan, List *outer_tlist,
+									  Plan *inner_plan, List *inner_tlist);
+static MergeJoin *create_mergejoin_plan(MergePath *best_path, List *tlist,
 										List *joinclauses, List *otherclauses,
-										Plan *outer_node, List *outer_tlist,
-										Plan *inner_node, List *inner_tlist);
-static HashJoin *create_hashjoin_node(HashPath *best_path, List *tlist,
+										Plan *outer_plan, List *outer_tlist,
+										Plan *inner_plan, List *inner_tlist);
+static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist,
 									  List *joinclauses, List *otherclauses,
-									  Plan *outer_node, List *outer_tlist,
-									  Plan *inner_node, List *inner_tlist);
+									  Plan *outer_plan, List *outer_tlist,
+									  Plan *inner_plan, List *inner_tlist);
 static List *fix_indxqual_references(List *indexquals, IndexPath *index_path);
 static List *fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
 					 Form_pg_index index);
 static Node *fix_indxqual_operand(Node *node, int baserelid,
 					 Form_pg_index index,
 					 Oid *opclass);
+static List *switch_outer(List *clauses);
+static void copy_path_costsize(Plan *dest, Path *src);
+static void copy_plan_costsize(Plan *dest, Plan *src);
 static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
 static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
 			   List *indxid, List *indxqual,
@@ -83,7 +86,6 @@ static MergeJoin *make_mergejoin(List *tlist,
 								 List *mergeclauses,
 								 Plan *lefttree, Plan *righttree,
 								 JoinType jointype);
-static void copy_path_costsize(Plan *dest, Path *src);
 
 /*
  * create_plan
@@ -98,13 +100,12 @@ static void copy_path_costsize(Plan *dest, Path *src);
  *
  *	  best_path is the best access path
  *
- *	  Returns the access plan.
+ *	  Returns a Plan tree.
  */
 Plan *
 create_plan(Query *root, Path *best_path)
 {
-	List	   *tlist = best_path->parent->targetlist;
-	Plan	   *plan_node = (Plan *) NULL;
+	Plan	   *plan;
 
 	switch (best_path->pathtype)
 	{
@@ -112,18 +113,22 @@ create_plan(Query *root, Path *best_path)
 		case T_SeqScan:
 		case T_TidScan:
 		case T_SubqueryScan:
-			plan_node = (Plan *) create_scan_node(root, best_path, tlist);
+			plan = (Plan *) create_scan_plan(root, best_path);
 			break;
 		case T_HashJoin:
 		case T_MergeJoin:
 		case T_NestLoop:
-			plan_node = (Plan *) create_join_node(root,
-												  (JoinPath *) best_path,
-												  tlist);
+			plan = (Plan *) create_join_plan(root,
+											 (JoinPath *) best_path);
+			break;
+		case T_Append:
+			plan = (Plan *) create_append_plan(root,
+											   (AppendPath *) best_path);
 			break;
 		default:
 			elog(ERROR, "create_plan: unknown pathtype %d",
 				 best_path->pathtype);
+			plan = NULL;		/* keep compiler quiet */
 			break;
 	}
 
@@ -131,30 +136,29 @@ create_plan(Query *root, Path *best_path)
 	/* sort clauses by cost/(1-selectivity) -- JMH 2/26/92 */
 	if (XfuncMode != XFUNC_OFF)
 	{
-		set_qpqual((Plan) plan_node,
-				   lisp_qsort(get_qpqual((Plan) plan_node),
+		set_qpqual((Plan) plan,
+				   lisp_qsort(get_qpqual((Plan) plan),
 							  xfunc_clause_compare));
 		if (XfuncMode != XFUNC_NOR)
 			/* sort the disjuncts within each clause by cost -- JMH 3/4/92 */
-			xfunc_disjunct_sort(plan_node->qpqual);
+			xfunc_disjunct_sort(plan->qpqual);
 	}
 #endif
 
-	return plan_node;
+	return plan;
 }
 
 /*
- * create_scan_node
- *	 Create a scan path for the parent relation of 'best_path'.
+ * create_scan_plan
+ *	 Create a scan plan for the parent relation of 'best_path'.
  *
- *	 tlist is the targetlist for the base relation scanned by 'best_path'
- *
- *	 Returns the scan node.
+ *	 Returns a Plan node.
  */
 static Scan *
-create_scan_node(Query *root, Path *best_path, List *tlist)
+create_scan_plan(Query *root, Path *best_path)
 {
-	Scan	   *node = NULL;
+	Scan	   *plan;
+	List	   *tlist = best_path->parent->targetlist;
 	List	   *scan_clauses;
 
 	/*
@@ -166,65 +170,64 @@ create_scan_node(Query *root, Path *best_path, List *tlist)
 	switch (best_path->pathtype)
 	{
 		case T_SeqScan:
-			node = (Scan *) create_seqscan_node(best_path,
+			plan = (Scan *) create_seqscan_plan(best_path,
 												tlist,
 												scan_clauses);
 			break;
 
 		case T_IndexScan:
-			node = (Scan *) create_indexscan_node(root,
+			plan = (Scan *) create_indexscan_plan(root,
 												  (IndexPath *) best_path,
 												  tlist,
 												  scan_clauses);
 			break;
 
 		case T_TidScan:
-			node = (Scan *) create_tidscan_node((TidPath *) best_path,
+			plan = (Scan *) create_tidscan_plan((TidPath *) best_path,
 												tlist,
 												scan_clauses);
 			break;
 
 		case T_SubqueryScan:
-			node = (Scan *) create_subqueryscan_node(best_path,
+			plan = (Scan *) create_subqueryscan_plan(best_path,
 													 tlist,
 													 scan_clauses);
 			break;
 
 		default:
-			elog(ERROR, "create_scan_node: unknown node type: %d",
+			elog(ERROR, "create_scan_plan: unknown node type: %d",
 				 best_path->pathtype);
+			plan = NULL;		/* keep compiler quiet */
 			break;
 	}
 
-	return node;
+	return plan;
 }
 
 /*
- * create_join_node
- *	  Create a join path for 'best_path' and(recursively) paths for its
+ * create_join_plan
+ *	  Create a join plan for 'best_path' and (recursively) plans for its
  *	  inner and outer paths.
  *
- *	  'tlist' is the targetlist for the join relation corresponding to
- *		'best_path'
- *
- *	  Returns the join node.
+ *	  Returns a Plan node.
  */
 static Join *
-create_join_node(Query *root, JoinPath *best_path, List *tlist)
+create_join_plan(Query *root, JoinPath *best_path)
 {
-	Plan	   *outer_node;
+	List	   *join_tlist = best_path->path.parent->targetlist;
+	Plan	   *outer_plan;
 	List	   *outer_tlist;
-	Plan	   *inner_node;
+	Plan	   *inner_plan;
 	List	   *inner_tlist;
 	List	   *joinclauses;
 	List	   *otherclauses;
-	Join	   *retval = NULL;
+	Join	   *plan;
 
-	outer_node = create_plan(root, best_path->outerjoinpath);
-	outer_tlist = outer_node->targetlist;
+	outer_plan = create_plan(root, best_path->outerjoinpath);
+	outer_tlist = outer_plan->targetlist;
 
-	inner_node = create_plan(root, best_path->innerjoinpath);
-	inner_tlist = inner_node->targetlist;
+	inner_plan = create_plan(root, best_path->innerjoinpath);
+	inner_tlist = inner_plan->targetlist;
 
 	if (IS_OUTER_JOIN(best_path->jointype))
 	{
@@ -241,38 +244,40 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
 	switch (best_path->path.pathtype)
 	{
 		case T_MergeJoin:
-			retval = (Join *) create_mergejoin_node((MergePath *) best_path,
-													tlist,
-													joinclauses,
-													otherclauses,
-													outer_node,
-													outer_tlist,
-													inner_node,
-													inner_tlist);
+			plan = (Join *) create_mergejoin_plan((MergePath *) best_path,
+												  join_tlist,
+												  joinclauses,
+												  otherclauses,
+												  outer_plan,
+												  outer_tlist,
+												  inner_plan,
+												  inner_tlist);
 			break;
 		case T_HashJoin:
-			retval = (Join *) create_hashjoin_node((HashPath *) best_path,
-												   tlist,
-												   joinclauses,
-												   otherclauses,
-												   outer_node,
-												   outer_tlist,
-												   inner_node,
-												   inner_tlist);
+			plan = (Join *) create_hashjoin_plan((HashPath *) best_path,
+												 join_tlist,
+												 joinclauses,
+												 otherclauses,
+												 outer_plan,
+												 outer_tlist,
+												 inner_plan,
+												 inner_tlist);
 			break;
 		case T_NestLoop:
-			retval = (Join *) create_nestloop_node((NestPath *) best_path,
-												   tlist,
-												   joinclauses,
-												   otherclauses,
-												   outer_node,
-												   outer_tlist,
-												   inner_node,
-												   inner_tlist);
+			plan = (Join *) create_nestloop_plan((NestPath *) best_path,
+												 join_tlist,
+												 joinclauses,
+												 otherclauses,
+												 outer_plan,
+												 outer_tlist,
+												 inner_plan,
+												 inner_tlist);
 			break;
 		default:
-			elog(ERROR, "create_join_node: unknown node type: %d",
+			elog(ERROR, "create_join_plan: unknown node type: %d",
 				 best_path->path.pathtype);
+			plan = NULL;		/* keep compiler quiet */
+			break;
 	}
 
 #ifdef NOT_USED
@@ -283,14 +288,42 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
 	 * JMH, 6/15/92
 	 */
 	if (get_loc_restrictinfo(best_path) != NIL)
-		set_qpqual((Plan) retval,
-				   nconc(get_qpqual((Plan) retval),
+		set_qpqual((Plan) plan,
+				   nconc(get_qpqual((Plan) plan),
 				   get_actual_clauses(get_loc_restrictinfo(best_path))));
 #endif
 
-	return retval;
+	return plan;
 }
 
+/*
+ * create_append_plan
+ *	  Create an Append plan for 'best_path' and (recursively) plans
+ *	  for its subpaths.
+ *
+ *	  Returns a Plan node.
+ */
+static Append *
+create_append_plan(Query *root, AppendPath *best_path)
+{
+	Append	   *plan;
+	List	   *tlist = best_path->path.parent->targetlist;
+	List	   *subplans = NIL;
+	List	   *subpaths;
+
+	foreach(subpaths, best_path->subpaths)
+	{
+		Path   *subpath = (Path *) lfirst(subpaths);
+		
+		subplans = lappend(subplans, create_plan(root, subpath));
+	}
+
+	plan = make_append(subplans, false, tlist);
+
+	return plan;
+}
+
+
 /*****************************************************************************
  *
  *	BASE-RELATION SCAN METHODS
@@ -299,14 +332,14 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
 
 
 /*
- * create_seqscan_node
- *	 Returns a seqscan node for the base relation scanned by 'best_path'
+ * create_seqscan_plan
+ *	 Returns a seqscan plan for the base relation scanned by 'best_path'
  *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  */
 static SeqScan *
-create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
+create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
 {
-	SeqScan    *scan_node;
+	SeqScan    *scan_plan;
 	Index		scan_relid;
 
 	/* there should be exactly one base rel involved... */
@@ -315,18 +348,18 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
 
 	scan_relid = (Index) lfirsti(best_path->parent->relids);
 
-	scan_node = make_seqscan(tlist,
+	scan_plan = make_seqscan(tlist,
 							 scan_clauses,
 							 scan_relid);
 
-	copy_path_costsize(&scan_node->plan, best_path);
+	copy_path_costsize(&scan_plan->plan, best_path);
 
-	return scan_node;
+	return scan_plan;
 }
 
 /*
- * create_indexscan_node
- *	  Returns a indexscan node for the base relation scanned by 'best_path'
+ * create_indexscan_plan
+ *	  Returns a indexscan plan for the base relation scanned by 'best_path'
  *	  with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  *
  * The indexqual of the path contains a sublist of implicitly-ANDed qual
@@ -338,7 +371,7 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
  * scan.
  */
 static IndexScan *
-create_indexscan_node(Query *root,
+create_indexscan_plan(Query *root,
 					  IndexPath *best_path,
 					  List *tlist,
 					  List *scan_clauses)
@@ -348,7 +381,7 @@ create_indexscan_node(Query *root,
 	List	   *qpqual;
 	List	   *fixed_indxqual;
 	List	   *ixid;
-	IndexScan  *scan_node;
+	IndexScan  *scan_plan;
 	bool		lossy = false;
 
 	/* there should be exactly one base rel involved... */
@@ -433,7 +466,7 @@ create_indexscan_node(Query *root,
 	 */
 	fixed_indxqual = fix_indxqual_references(indxqual, best_path);
 
-	scan_node = make_indexscan(tlist,
+	scan_plan = make_indexscan(tlist,
 							   qpqual,
 							   baserelid,
 							   best_path->indexid,
@@ -441,22 +474,22 @@ create_indexscan_node(Query *root,
 							   indxqual,
 							   best_path->indexscandir);
 
-	copy_path_costsize(&scan_node->scan.plan, &best_path->path);
+	copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
 	/* use the indexscan-specific rows estimate, not the parent rel's */
-	scan_node->scan.plan.plan_rows = best_path->rows;
+	scan_plan->scan.plan.plan_rows = best_path->rows;
 
-	return scan_node;
+	return scan_plan;
 }
 
 /*
- * create_tidscan_node
- *	 Returns a tidscan node for the base relation scanned by 'best_path'
+ * create_tidscan_plan
+ *	 Returns a tidscan plan for the base relation scanned by 'best_path'
  *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  */
 static TidScan *
-create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses)
+create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses)
 {
-	TidScan    *scan_node;
+	TidScan    *scan_plan;
 	Index		scan_relid;
 
 	/* there should be exactly one base rel involved... */
@@ -465,28 +498,28 @@ create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses)
 
 	scan_relid = (Index) lfirsti(best_path->path.parent->relids);
 
-	scan_node = make_tidscan(tlist,
+	scan_plan = make_tidscan(tlist,
 							 scan_clauses,
 							 scan_relid,
 							 best_path->tideval);
 
 	if (best_path->unjoined_relids)
-		scan_node->needRescan = true;
+		scan_plan->needRescan = true;
 
-	copy_path_costsize(&scan_node->scan.plan, &best_path->path);
+	copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
 
-	return scan_node;
+	return scan_plan;
 }
 
 /*
- * create_subqueryscan_node
- *	 Returns a subqueryscan node for the base relation scanned by 'best_path'
+ * create_subqueryscan_plan
+ *	 Returns a subqueryscan plan for the base relation scanned by 'best_path'
  *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  */
 static SubqueryScan *
-create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses)
+create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
 {
-	SubqueryScan *scan_node;
+	SubqueryScan *scan_plan;
 	Index		scan_relid;
 
 	/* there should be exactly one base rel involved... */
@@ -496,14 +529,12 @@ create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses)
 
 	scan_relid = (Index) lfirsti(best_path->parent->relids);
 
-	scan_node = make_subqueryscan(tlist,
+	scan_plan = make_subqueryscan(tlist,
 								  scan_clauses,
 								  scan_relid,
 								  best_path->parent->subplan);
 
-	copy_path_costsize(&scan_node->scan.plan, best_path);
-
-	return scan_node;
+	return scan_plan;
 }
 
 /*****************************************************************************
@@ -528,18 +559,18 @@ create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses)
  *****************************************************************************/
 
 static NestLoop *
-create_nestloop_node(NestPath *best_path,
+create_nestloop_plan(NestPath *best_path,
 					 List *tlist,
 					 List *joinclauses,
 					 List *otherclauses,
-					 Plan *outer_node,
+					 Plan *outer_plan,
 					 List *outer_tlist,
-					 Plan *inner_node,
+					 Plan *inner_plan,
 					 List *inner_tlist)
 {
-	NestLoop   *join_node;
+	NestLoop   *join_plan;
 
-	if (IsA(inner_node, IndexScan))
+	if (IsA(inner_plan, IndexScan))
 	{
 
 		/*
@@ -563,7 +594,7 @@ create_nestloop_node(NestPath *best_path,
 		 * and therefore has not itself done join_references renumbering
 		 * of the vars in its quals.
 		 */
-		IndexScan  *innerscan = (IndexScan *) inner_node;
+		IndexScan  *innerscan = (IndexScan *) inner_plan;
 		List	   *indxqualorig = innerscan->indxqualorig;
 
 		/* No work needed if indxqual refers only to its own relation... */
@@ -591,23 +622,23 @@ create_nestloop_node(NestPath *best_path,
 												  NIL,
 												  innerrel);
 			/* fix the inner qpqual too, if it has join clauses */
-			if (NumRelids((Node *) inner_node->qual) > 1)
-				inner_node->qual = join_references(inner_node->qual,
+			if (NumRelids((Node *) inner_plan->qual) > 1)
+				inner_plan->qual = join_references(inner_plan->qual,
 												   outer_tlist,
 												   NIL,
 												   innerrel);
 		}
 	}
-	else if (IsA(inner_node, TidScan))
+	else if (IsA(inner_plan, TidScan))
 	{
-		TidScan    *innerscan = (TidScan *) inner_node;
+		TidScan    *innerscan = (TidScan *) inner_plan;
 
 		innerscan->tideval = join_references(innerscan->tideval,
 											 outer_tlist,
 											 inner_tlist,
 											 innerscan->scan.scanrelid);
 	}
-	else if (IsA_Join(inner_node))
+	else if (IsA_Join(inner_plan))
 	{
 
 		/*
@@ -617,8 +648,8 @@ create_nestloop_node(NestPath *best_path,
 		 * join --- how can we estimate whether this is a good thing to
 		 * do?
 		 */
-		inner_node = (Plan *) make_material(inner_tlist,
-											inner_node);
+		inner_plan = (Plan *) make_material(inner_tlist,
+											inner_plan);
 	}
 
 	/*
@@ -633,30 +664,30 @@ create_nestloop_node(NestPath *best_path,
 								   inner_tlist,
 								   (Index) 0);
 
-	join_node = make_nestloop(tlist,
+	join_plan = make_nestloop(tlist,
 							  joinclauses,
 							  otherclauses,
-							  outer_node,
-							  inner_node,
+							  outer_plan,
+							  inner_plan,
 							  best_path->jointype);
 
-	copy_path_costsize(&join_node->join.plan, &best_path->path);
+	copy_path_costsize(&join_plan->join.plan, &best_path->path);
 
-	return join_node;
+	return join_plan;
 }
 
 static MergeJoin *
-create_mergejoin_node(MergePath *best_path,
+create_mergejoin_plan(MergePath *best_path,
 					  List *tlist,
 					  List *joinclauses,
 					  List *otherclauses,
-					  Plan *outer_node,
+					  Plan *outer_plan,
 					  List *outer_tlist,
-					  Plan *inner_node,
+					  Plan *inner_plan,
 					  List *inner_tlist)
 {
 	List	   *mergeclauses;
-	MergeJoin  *join_node;
+	MergeJoin  *join_plan;
 
 	mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
 
@@ -692,15 +723,15 @@ create_mergejoin_node(MergePath *best_path,
 	 * necessary.  The sort cost was already accounted for in the path.
 	 */
 	if (best_path->outersortkeys)
-		outer_node = (Plan *)
+		outer_plan = (Plan *)
 			make_sort_from_pathkeys(outer_tlist,
-									outer_node,
+									outer_plan,
 									best_path->outersortkeys);
 
 	if (best_path->innersortkeys)
-		inner_node = (Plan *)
+		inner_plan = (Plan *)
 			make_sort_from_pathkeys(inner_tlist,
-									inner_node,
+									inner_plan,
 									best_path->innersortkeys);
 
 	/*
@@ -723,7 +754,7 @@ create_mergejoin_node(MergePath *best_path,
 	 * This check must agree with ExecMarkPos/ExecRestrPos in
 	 * executor/execAmi.c!
 	 */
-	switch (nodeTag(inner_node))
+	switch (nodeTag(inner_plan))
 	{
 		case T_SeqScan:
 		case T_IndexScan:
@@ -734,40 +765,40 @@ create_mergejoin_node(MergePath *best_path,
 
 		default:
 			/* Ooops, need to materialize the inner plan */
-			inner_node = (Plan *) make_material(inner_tlist,
-												inner_node);
+			inner_plan = (Plan *) make_material(inner_tlist,
+												inner_plan);
 			break;
 	}
 
 	/*
 	 * Now we can build the mergejoin node.
 	 */
-	join_node = make_mergejoin(tlist,
+	join_plan = make_mergejoin(tlist,
 							   joinclauses,
 							   otherclauses,
 							   mergeclauses,
-							   outer_node,
-							   inner_node,
+							   outer_plan,
+							   inner_plan,
 							   best_path->jpath.jointype);
 
-	copy_path_costsize(&join_node->join.plan, &best_path->jpath.path);
+	copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
 
-	return join_node;
+	return join_plan;
 }
 
 static HashJoin *
-create_hashjoin_node(HashPath *best_path,
+create_hashjoin_plan(HashPath *best_path,
 					 List *tlist,
 					 List *joinclauses,
 					 List *otherclauses,
-					 Plan *outer_node,
+					 Plan *outer_plan,
 					 List *outer_tlist,
-					 Plan *inner_node,
+					 Plan *inner_plan,
 					 List *inner_tlist)
 {
 	List	   *hashclauses;
-	HashJoin   *join_node;
-	Hash	   *hash_node;
+	HashJoin   *join_plan;
+	Hash	   *hash_plan;
 	Node	   *innerhashkey;
 
 	/*
@@ -811,18 +842,18 @@ create_hashjoin_node(HashPath *best_path,
 	/*
 	 * Build the hash node and hash join node.
 	 */
-	hash_node = make_hash(inner_tlist, innerhashkey, inner_node);
-	join_node = make_hashjoin(tlist,
+	hash_plan = make_hash(inner_tlist, innerhashkey, inner_plan);
+	join_plan = make_hashjoin(tlist,
 							  joinclauses,
 							  otherclauses,
 							  hashclauses,
-							  outer_node,
-							  (Plan *) hash_node,
+							  outer_plan,
+							  (Plan *) hash_plan,
 							  best_path->jpath.jointype);
 
-	copy_path_costsize(&join_node->join.plan, &best_path->jpath.path);
+	copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
 
-	return join_node;
+	return join_plan;
 }
 
 
@@ -1106,7 +1137,7 @@ copy_path_costsize(Plan *dest, Path *src)
  * but it helps produce more reasonable-looking EXPLAIN output.
  * (Some callers alter the info after copying it.)
  */
-void
+static void
 copy_plan_costsize(Plan *dest, Plan *src)
 {
 	if (src)
@@ -1128,6 +1159,10 @@ copy_plan_costsize(Plan *dest, Plan *src)
 
 /*****************************************************************************
  *
+ *	PLAN NODE BUILDING ROUTINES
+ *
+ * Some of these are exported because they are called to build plan nodes
+ * in contexts where we're not deriving the plan node from a path node.
  *
  *****************************************************************************/
 
@@ -1212,7 +1247,7 @@ make_subqueryscan(List *qptlist,
 	SubqueryScan *node = makeNode(SubqueryScan);
 	Plan	   *plan = &node->scan.plan;
 
-	/* cost should be inserted by caller */
+	copy_plan_costsize(plan, subplan);
 	plan->state = (EState *) NULL;
 	plan->targetlist = qptlist;
 	plan->qual = qpqual;
@@ -1225,6 +1260,39 @@ make_subqueryscan(List *qptlist,
 	return node;
 }
 
+Append *
+make_append(List *appendplans, bool isTarget, List *tlist)
+{
+	Append	   *node = makeNode(Append);
+	Plan	   *plan = &node->plan;
+	List	   *subnode;
+
+	/* compute costs from subplan costs */
+	plan->startup_cost = 0;
+	plan->total_cost = 0;
+	plan->plan_rows = 0;
+	plan->plan_width = 0;
+	foreach(subnode, appendplans)
+	{
+		Plan	   *subplan = (Plan *) lfirst(subnode);
+
+		if (subnode == appendplans)		/* first node? */
+			plan->startup_cost = subplan->startup_cost;
+		plan->total_cost += subplan->total_cost;
+		plan->plan_rows += subplan->plan_rows;
+		if (plan->plan_width < subplan->plan_width)
+			plan->plan_width = subplan->plan_width;
+	}
+	plan->state = (EState *) NULL;
+	plan->targetlist = tlist;
+	plan->qual = NIL;
+	plan->lefttree = NULL;
+	plan->righttree = NULL;
+	node->appendplans = appendplans;
+	node->isTarget = isTarget;
+
+	return node;
+}
 
 static NestLoop *
 make_nestloop(List *tlist,
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index a9747b32799..1a923a506ff 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.61 2000/10/05 19:11:29 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.62 2000/11/12 00:36:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -65,7 +65,8 @@ static Plan *subplanner(Query *root, List *flat_tlist,
  *	  tuple_fraction >= 1: tuple_fraction is the absolute number of tuples
  *		expected to be retrieved (ie, a LIMIT specification)
  * Note that while this routine and its subroutines treat a negative
- * tuple_fraction the same as 0, union_planner has a different interpretation.
+ * tuple_fraction the same as 0, grouping_planner has a different
+ * interpretation.
  *
  * Returns a query plan.
  *--------------------
@@ -125,9 +126,16 @@ query_planner(Query *root,
 	subplan = subplanner(root, var_only_tlist, tuple_fraction);
 
 	/*
-	 * Build a result node to control the plan if we have constant quals.
+	 * Build a result node to control the plan if we have constant quals,
+	 * or if the top-level plan node is one that cannot do expression
+	 * evaluation (it won't be able to evaluate the requested tlist).
+	 * Currently, the only plan node we might see here that falls into
+	 * that category is Append.
+	 *
+	 * XXX future improvement: if the given tlist is flat anyway, we don't
+	 * really need a Result node.
 	 */
-	if (constant_quals)
+	if (constant_quals || IsA(subplan, Append))
 	{
 
 		/*
@@ -325,8 +333,8 @@ subplanner(Query *root,
 
 	/*
 	 * Nothing for it but to sort the cheapest-total-cost path --- but we
-	 * let the caller do that.	union_planner has to be able to add a sort
-	 * node anyway, so no need for extra code here.  (Furthermore, the
+	 * let the caller do that.	grouping_planner has to be able to add a
+	 * sort node anyway, so no need for extra code here.  (Furthermore, the
 	 * given pathkeys might involve something we can't compute here, such
 	 * as an aggregate function...)
 	 */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 67089e68d29..7a1151f0c9a 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.95 2000/11/09 02:46:16 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.96 2000/11/12 00:36:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -43,6 +43,8 @@ static void resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist);
 static Node *preprocess_jointree(Query *parse, Node *jtnode);
 static Node *preprocess_expression(Query *parse, Node *expr, int kind);
 static void preprocess_qual_conditions(Query *parse, Node *jtnode);
+static Plan *inheritance_planner(Query *parse, List *inheritlist);
+static Plan *grouping_planner(Query *parse, double tuple_fraction);
 static List *make_subplanTargetList(Query *parse, List *tlist,
 					   AttrNumber **groupColIdx);
 static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
@@ -65,7 +67,7 @@ planner(Query *parse)
 
 	/*
 	 * The planner can be called recursively (an example is when
-	 * eval_const_expressions tries to simplify an SQL function).
+	 * eval_const_expressions tries to pre-evaluate an SQL function).
 	 * So, these global state variables must be saved and restored.
 	 *
 	 * These vars cannot be moved into the Query structure since their
@@ -109,11 +111,14 @@ planner(Query *parse)
  *
  * parse is the querytree produced by the parser & rewriter.
  * tuple_fraction is the fraction of tuples we expect will be retrieved.
- * tuple_fraction is interpreted as explained for union_planner, below.
+ * tuple_fraction is interpreted as explained for grouping_planner, below.
  *
  * Basically, this routine does the stuff that should only be done once
- * per Query object.  It then calls union_planner, which may be called
- * recursively on the same Query node in order to handle inheritance.
+ * per Query object.  It then calls grouping_planner.  At one time,
+ * grouping_planner could be invoked recursively on the same Query object;
+ * that's not currently true, but we keep the separation between the two
+ * routines anyway, in case we need it again someday.
+ *
  * subquery_planner will be called recursively to handle sub-Query nodes
  * found within the query's expressions and rangetable.
  *
@@ -164,7 +169,7 @@ subquery_planner(Query *parse, double tuple_fraction)
 	}
 
 	/*
-	 * Do preprocessing on targetlist and quals.
+	 * Do expression preprocessing on targetlist and quals.
 	 */
 	parse->targetList = (List *)
 		preprocess_expression(parse, (Node *) parse->targetList,
@@ -176,17 +181,14 @@ subquery_planner(Query *parse, double tuple_fraction)
 											  EXPRKIND_HAVING);
 
 	/*
-	 * Do the main planning (potentially recursive for inheritance)
-	 */
-	plan = union_planner(parse, tuple_fraction);
-
-	/*
-	 * XXX should any more of union_planner's activity be moved here?
-	 *
-	 * That would take careful study of the interactions with prepunion.c,
-	 * but I suspect it would pay off in simplicity and avoidance of
-	 * wasted cycles.
+	 * Do the main planning.  If we have an inherited target relation,
+	 * that needs special processing, else go straight to grouping_planner.
 	 */
+	if (parse->resultRelation &&
+		(lst = expand_inherted_rtentry(parse, parse->resultRelation)) != NIL)
+		plan = inheritance_planner(parse, lst);
+	else
+		plan = grouping_planner(parse, tuple_fraction);
 
 	/*
 	 * If any subplans were generated, or if we're inside a subplan,
@@ -600,10 +602,65 @@ preprocess_qual_conditions(Query *parse, Node *jtnode)
 }
 
 /*--------------------
- * union_planner
- *	  Invokes the planner on union-type queries (both set operations and
- *	  appends produced by inheritance), recursing if necessary to get them
- *	  all, then processes normal plans.
+ * inheritance_planner
+ *	  Generate a plan in the case where the result relation is an
+ *	  inheritance set.
+ *
+ * We have to handle this case differently from cases where a source
+ * relation is an inheritance set.  Source inheritance is expanded at
+ * the bottom of the plan tree (see allpaths.c), but target inheritance
+ * has to be expanded at the top.  The reason is that for UPDATE, each
+ * target relation needs a different targetlist matching its own column
+ * set.  (This is not so critical for DELETE, but for simplicity we treat
+ * inherited DELETE the same way.)  Fortunately, the UPDATE/DELETE target
+ * can never be the nullable side of an outer join, so it's OK to generate
+ * the plan this way.
+ *
+ * parse is the querytree produced by the parser & rewriter.
+ * inheritlist is an integer list of RT indexes for the result relation set.
+ *
+ * Returns a query plan.
+ *--------------------
+ */
+static Plan *
+inheritance_planner(Query *parse, List *inheritlist)
+{
+	int			parentRTindex = parse->resultRelation;
+	Oid			parentOID = getrelid(parentRTindex, parse->rtable);
+	List	   *subplans = NIL;
+	List	   *tlist = NIL;
+	List	   *l;
+
+	foreach(l, inheritlist)
+	{
+		int		childRTindex = lfirsti(l);
+		Oid		childOID = getrelid(childRTindex, parse->rtable);
+		Query  *subquery;
+		Plan   *subplan;
+
+		/* Generate modified query with this rel as target */
+		subquery = (Query *) adjust_inherited_attrs((Node *) parse,
+													parentRTindex, parentOID,
+													childRTindex, childOID);
+		/* Generate plan */
+		subplan = grouping_planner(subquery, 0.0 /* retrieve all tuples */);
+		subplans = lappend(subplans, subplan);
+		/* Save preprocessed tlist from first rel for use in Append */
+		if (tlist == NIL)
+			tlist = subplan->targetlist;
+	}
+
+	/* Save the target-relations list for the executor, too */
+	parse->resultRelations = inheritlist;
+
+	return (Plan *) make_append(subplans, true, tlist);
+}
+
+/*--------------------
+ * grouping_planner
+ *	  Perform planning steps related to grouping, aggregation, etc.
+ *	  This primarily means adding top-level processing to the basic
+ *	  query plan produced by query_planner.
  *
  * parse is the querytree produced by the parser & rewriter.
  * tuple_fraction is the fraction of tuples we expect will be retrieved
@@ -621,18 +678,15 @@ preprocess_qual_conditions(Query *parse, Node *jtnode)
  * Returns a query plan.
  *--------------------
  */
-Plan *
-union_planner(Query *parse,
-			  double tuple_fraction)
+static Plan *
+grouping_planner(Query *parse, double tuple_fraction)
 {
 	List	   *tlist = parse->targetList;
-	Plan	   *result_plan = (Plan *) NULL;
-	AttrNumber *groupColIdx = NULL;
-	List	   *current_pathkeys = NIL;
+	Plan	   *result_plan;
+	List	   *current_pathkeys;
 	List	   *group_pathkeys;
 	List	   *sort_pathkeys;
-	Index		rt_index;
-	List	   *inheritors;
+	AttrNumber *groupColIdx = NULL;
 
 	if (parse->setOperations)
 	{
@@ -654,12 +708,13 @@ union_planner(Query *parse,
 		tlist = postprocess_setop_tlist(result_plan->targetlist, tlist);
 
 		/*
-		 * We leave current_pathkeys NIL indicating we do not know sort
+		 * We set current_pathkeys NIL indicating we do not know sort
 		 * order.  This is correct when the top set operation is UNION ALL,
 		 * since the appended-together results are unsorted even if the
 		 * subplans were sorted.  For other set operations we could be
-		 * smarter --- future improvement!
+		 * smarter --- room for future improvement!
 		 */
+		current_pathkeys = NIL;
 
 		/*
 		 * Calculate pathkeys that represent grouping/ordering
@@ -670,54 +725,6 @@ union_planner(Query *parse,
 		sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,
 													  tlist);
 	}
-	else if (find_inheritable_rt_entry(parse->rtable,
-									   &rt_index, &inheritors))
-	{
-		List	   *sub_tlist;
-
-		/*
-		 * Generate appropriate target list for subplan; may be different
-		 * from tlist if grouping or aggregation is needed.
-		 */
-		sub_tlist = make_subplanTargetList(parse, tlist, &groupColIdx);
-
-		/*
-		 * Recursively plan the subqueries needed for inheritance
-		 */
-		result_plan = plan_inherit_queries(parse, sub_tlist,
-										   rt_index, inheritors);
-
-		/*
-		 * Fix up outer target list.  NOTE: unlike the case for
-		 * non-inherited query, we pass the unfixed tlist to subplans,
-		 * which do their own fixing.  But we still want to fix the outer
-		 * target list afterwards. I *think* this is correct --- doing the
-		 * fix before recursing is definitely wrong, because
-		 * preprocess_targetlist() will do the wrong thing if invoked
-		 * twice on the same list. Maybe that is a bug? tgl 6/6/99
-		 */
-		tlist = preprocess_targetlist(tlist,
-									  parse->commandType,
-									  parse->resultRelation,
-									  parse->rtable);
-
-		if (parse->rowMarks)
-			elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries");
-
-		/*
-		 * We leave current_pathkeys NIL indicating we do not know sort
-		 * order of the Append-ed results.
-		 */
-
-		/*
-		 * Calculate pathkeys that represent grouping/ordering
-		 * requirements
-		 */
-		group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause,
-													   tlist);
-		sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,
-													  tlist);
-	}
 	else
 	{
 		List	   *sub_tlist;
@@ -938,10 +945,6 @@ union_planner(Query *parse,
 		current_pathkeys = parse->query_pathkeys;
 	}
 
-	/* query_planner returns NULL if it thinks plan is bogus */
-	if (!result_plan)
-		elog(ERROR, "union_planner: failed to create plan");
-
 	/*
 	 * We couldn't canonicalize group_pathkeys and sort_pathkeys before
 	 * running query_planner(), so do it now.
@@ -1057,7 +1060,7 @@ union_planner(Query *parse,
  * make_subplanTargetList
  *	  Generate appropriate target list when grouping is required.
  *
- * When union_planner inserts Aggregate and/or Group plan nodes above
+ * When grouping_planner inserts Aggregate and/or Group plan nodes above
  * the result of query_planner, we typically want to pass a different
  * target list to query_planner than the outer plan nodes should have.
  * This routine generates the correct target list for the subplan.
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index ea9b902bc5b..ebb09f59393 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1,15 +1,20 @@
 /*-------------------------------------------------------------------------
  *
  * prepunion.c
- *	  Routines to plan set-operation and inheritance queries.  The filename
- *	  is a leftover from a time when only UNIONs were handled.
+ *	  Routines to plan set-operation queries.  The filename is a leftover
+ *	  from a time when only UNIONs were implemented.
+ *
+ * There is also some code here to support planning of queries that use
+ * inheritance (SELECT FROM foo*).  This no longer has much connection
+ * to the processing of UNION queries, but it's still here.
+ *
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.55 2000/11/09 02:46:17 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.56 2000/11/12 00:36:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,13 +35,19 @@
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
 
+/* macros borrowed from expression_tree_mutator */
+
+#define FLATCOPY(newnode, node, nodetype)  \
+	( (newnode) = makeNode(nodetype), \
+	  memcpy((newnode), (node), sizeof(nodetype)) )
+
 typedef struct
 {
-	Index		rt_index;
-	int			sublevels_up;
+	Index		old_rt_index;
+	Index		new_rt_index;
 	Oid			old_relid;
 	Oid			new_relid;
-} fix_parsetree_attnums_context;
+} adjust_inherited_attrs_context;
 
 static Plan *recurse_set_operations(Node *setOp, Query *parse,
 									List *colTypes, bool junkOK,
@@ -53,14 +64,8 @@ static List *generate_setop_tlist(List *colTypes, int flag,
 								  List *input_tlist,
 								  List *refnames_tlist);
 static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
-static void fix_parsetree_attnums(Index rt_index, Oid old_relid,
-					  Oid new_relid, Query *parsetree);
-static bool fix_parsetree_attnums_walker(Node *node,
-							 fix_parsetree_attnums_context *context);
-static RangeTblEntry *new_rangetable_entry(Oid new_relid,
-					 RangeTblEntry *old_entry);
-static Append *make_append(List *appendplans, Index rt_index,
-						   List *inheritrtable, List *tlist);
+static Node *adjust_inherited_attrs_mutator(Node *node,
+								adjust_inherited_attrs_context *context);
 
 
 /*
@@ -69,8 +74,8 @@ static Append *make_append(List *appendplans, Index rt_index,
  *	  Plans the queries for a tree of set operations (UNION/INTERSECT/EXCEPT)
  *
  * This routine only deals with the setOperations tree of the given query.
- * Any top-level ORDER BY requested in parse->sortClause will be added on
- * back in union_planner.
+ * Any top-level ORDER BY requested in parse->sortClause will be added
+ * when we return to grouping_planner.
  */
 Plan *
 plan_set_operations(Query *parse)
@@ -142,7 +147,6 @@ recurse_set_operations(Node *setOp, Query *parse,
 							  NIL,
 							  rtr->rtindex,
 							  subplan);
-		copy_plan_costsize(plan, subplan);
 		return plan;
 	}
 	else if (IsA(setOp, SetOperationStmt))
@@ -217,8 +221,7 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
 	 */
 	plan = (Plan *)
 		make_append(planlist,
-					0,
-					NIL,
+					false,
 					generate_setop_tlist(op->colTypes, -1, false,
 									((Plan *) lfirst(planlist))->targetlist,
 									refnames_tlist));
@@ -269,8 +272,7 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
 	 */
 	plan = (Plan *)
 		make_append(makeList2(lplan, rplan),
-					0,
-					NIL,
+					false,
 					generate_setop_tlist(op->colTypes, 0, false,
 										 lplan->targetlist,
 										 refnames_tlist));
@@ -456,132 +458,6 @@ tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
 }
 
 
-/*
- * plan_inherit_queries
- *	  Plans the queries for an inheritance tree rooted at a parent relation.
- *
- * Inputs:
- *	root = parent parse tree
- *	tlist = target list for inheritance subqueries (not same as parent's!)
- *	rt_index = rangetable index for current inheritance item
- *	inheritors = list of OIDs of the target rel plus all its descendants
- *
- * Returns an APPEND node that forms the result of performing the given
- * query for each member relation of the inheritance group.
- *
- * If grouping, aggregation, or sorting is specified in the parent plan,
- * the subplans should not do any of those steps --- we must do those
- * operations just once above the APPEND node.	The given tlist has been
- * modified appropriately to remove group/aggregate expressions, but the
- * Query node still has the relevant fields set.  We remove them in the
- * copies used for subplans.
- *
- * NOTE: this can be invoked recursively if more than one inheritance wildcard
- * is present.	At each level of recursion, the first wildcard remaining in
- * the rangetable is expanded.
- *
- * NOTE: don't bother optimizing this routine for the case that the target
- * rel has no children.  We won't get here unless find_inheritable_rt_entry
- * found at least two members in the inheritance group, so an APPEND is
- * certainly necessary.
- */
-Plan *
-plan_inherit_queries(Query *root, List *tlist,
-					 Index rt_index, List *inheritors)
-{
-	RangeTblEntry *rt_entry = rt_fetch(rt_index, root->rtable);
-	List	   *union_plans = NIL;
-	List	   *union_rtentries = NIL;
-	List	   *save_tlist = root->targetList;
-	double		tuple_fraction;
-	List	   *i;
-
-	/*
-	 * Avoid making copies of the root's tlist, which we aren't going to
-	 * use anyway (we are going to make copies of the passed tlist,
-	 * instead).  This is purely a space-saving hack.  Note we restore
-	 * the root's tlist before exiting.
-	 */
-	root->targetList = NIL;
-
-	/*
-	 * If we are going to need sorting or grouping at the top level, force
-	 * lower-level planners to assume that all tuples will be retrieved.
-	 */
-	if (root->distinctClause || root->sortClause ||
-		root->groupClause || root->hasAggs)
-		tuple_fraction = 0.0;	/* will need all tuples from each subplan */
-	else
-		tuple_fraction = -1.0;	/* default behavior is OK (I think) */
-
-	foreach(i, inheritors)
-	{
-		Oid			relid = lfirsti(i);
-
-		/*
-		 * Make a modifiable copy of the original query, and replace the
-		 * target rangetable entry in it with a new one identifying this
-		 * child table.  The new rtentry is marked inh = false --- this
-		 * is essential to prevent infinite recursion when the subquery
-		 * is rescanned by find_inheritable_rt_entry!
-		 */
-		Query	   *new_root = copyObject(root);
-		RangeTblEntry *new_rt_entry = new_rangetable_entry(relid,
-														   rt_entry);
-
-		new_rt_entry->inh = false;
-		rt_store(rt_index, new_root->rtable, new_rt_entry);
-
-		/*
-		 * Insert (a modifiable copy of) the desired simplified tlist into
-		 * the subquery
-		 */
-		new_root->targetList = copyObject(tlist);
-
-		/*
-		 * Clear the sorting and grouping qualifications in the subquery,
-		 * so that sorting will only be done once after append
-		 */
-		new_root->distinctClause = NIL;
-		new_root->sortClause = NIL;
-		new_root->groupClause = NIL;
-		new_root->havingQual = NULL;
-		new_root->limitOffset = NULL;	/* LIMIT's probably unsafe too */
-		new_root->limitCount = NULL;
-		new_root->hasAggs = false;		/* shouldn't be any left ... */
-
-		/*
-		 * Update attribute numbers in case child has different ordering
-		 * of columns than parent (as can happen after ALTER TABLE).
-		 *
-		 * XXX This is a crock, and it doesn't really work.  It'd be better
-		 * to fix ALTER TABLE to preserve consistency of attribute
-		 * numbering.
-		 */
-		fix_parsetree_attnums(rt_index,
-							  rt_entry->relid,
-							  relid,
-							  new_root);
-
-		/*
-		 * Plan the subquery by recursively calling union_planner().
-		 * Add plan and child rtentry to lists for APPEND.
-		 */
-		union_plans = lappend(union_plans,
-							  union_planner(new_root, tuple_fraction));
-		union_rtentries = lappend(union_rtentries, new_rt_entry);
-	}
-
-	/* Restore root's tlist */
-	root->targetList = save_tlist;
-
-	/* Construct the finished Append plan. */
-	return (Plan *) make_append(union_plans,
-								rt_index,
-								union_rtentries,
-								((Plan *) lfirst(union_plans))->targetlist);
-}
-
 /*
  * find_all_inheritors -
  *		Returns an integer list of relids including the given rel plus
@@ -622,200 +498,181 @@ find_all_inheritors(Oid parentrel)
 }
 
 /*
- * find_inheritable_rt_entry -
- *		Given a rangetable, find the first rangetable entry that represents
- *		an inheritance set.
+ * expand_inherted_rtentry
+ *		Check whether a rangetable entry represents an inheritance set.
+ *		If so, add entries for all the child tables to the query's
+ *		rangetable, and return an integer list of RT indexes for the
+ *		whole inheritance set (parent and children).
+ *		If not, return NIL.
  *
- *		If successful, set *rt_index to the index (1..n) of the entry,
- *		set *inheritors to a list of the relation OIDs of the set,
- *		and return TRUE.
+ * A childless table is never considered to be an inheritance set; therefore
+ * the result will never be a one-element list.  It'll be either empty
+ * or have two or more elements.
  *
- *		If there is no entry that requires inheritance processing,
- *		return FALSE.
- *
- * NOTE: We return the inheritors list so that plan_inherit_queries doesn't
- * have to compute it again.
- *
- * NOTE: We clear the inh flag in any entries that have it set but turn
- * out not to have any actual inheritance children.  This is an efficiency
- * hack to avoid having to repeat the inheritance checks if the list is
- * scanned again (as will happen during expansion of any subsequent entry
- * that does have inheritance children).  Although modifying the input
- * rangetable in-place may seem uncool, there's no reason not to do it,
- * since any re-examination of the entry would just come to the same
- * conclusion that the table has no children.
+ * NOTE: after this routine executes, the specified RTE will always have
+ * its inh flag cleared, whether or not there were any children.  This
+ * ensures we won't expand the same RTE twice, which would otherwise occur
+ * for the case of an inherited UPDATE/DELETE target relation.
  */
-bool
-find_inheritable_rt_entry(List *rangetable,
-						  Index *rt_index,
-						  List **inheritors)
+List *
+expand_inherted_rtentry(Query *parse, Index rti)
 {
-	Index		count = 0;
-	List	   *temp;
+	RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
+	Oid			parentOID = rte->relid;
+	List	   *inhOIDs;
+	List	   *inhRTIs;
+	List	   *l;
 
-	foreach(temp, rangetable)
+	/* Does RT entry allow inheritance? */
+	if (! rte->inh)
+		return NIL;
+	Assert(parentOID != InvalidOid && rte->subquery == NULL);
+	/* Always clear the parent's inh flag, see above comments */
+	rte->inh = false;
+	/* Fast path for common case of childless table */
+	if (! has_subclass(parentOID))
+		return NIL;
+	/* Scan for all members of inheritance set */
+	inhOIDs = find_all_inheritors(parentOID);
+	/*
+	 * Check that there's at least one descendant, else treat as
+	 * no-child case.  This could happen despite above has_subclass()
+	 * check, if table once had a child but no longer does.
+	 */
+	if (lnext(inhOIDs) == NIL)
+		return NIL;
+	/* OK, it's an inheritance set; expand it */
+	inhRTIs = makeListi1(rti);
+	foreach(l, inhOIDs)
 	{
-		RangeTblEntry  *rt_entry = (RangeTblEntry *) lfirst(temp);
-		List		   *inhs;
+		Oid		childOID = (Oid) lfirsti(l);
+		RangeTblEntry *childrte;
+		Index	childRTindex;
 
-		count++;
-		/* Ignore non-inheritable RT entries */
-		if (! rt_entry->inh)
+		/* parent will be in the list too, so ignore it */
+		if (childOID == parentOID)
 			continue;
-		/* Fast path for common case of childless table */
-		if (! has_subclass(rt_entry->relid))
-		{
-			rt_entry->inh = false;
-			continue;
-		}
-		/* Scan for all members of inheritance set */
-		inhs = find_all_inheritors(rt_entry->relid);
+
 		/*
-		 * Check that there's at least one descendant, else treat as
-		 * no-child case.  This could happen despite above has_subclass()
-		 * check, if table once had a child but no longer does.
+		 * Build an RTE for the child, and attach to query's rangetable list.
+		 * We copy most fields of the parent's RTE, but replace relation
+		 * real name and OID.  Note that inh will be false at this point.
 		 */
-		if (lnext(inhs) == NIL)
-		{
-			rt_entry->inh = false;
-			continue;
-		}
-		/* OK, found our boy */
-		*rt_index = count;
-		*inheritors = inhs;
-		return true;
+		childrte = copyObject(rte);
+		childrte->relname = get_rel_name(childOID);
+		childrte->relid = childOID;
+		parse->rtable = lappend(parse->rtable, childrte);
+		childRTindex = length(parse->rtable);
+
+		inhRTIs = lappendi(inhRTIs, childRTindex);
+	}
+	return inhRTIs;
+}
+
+/*
+ * adjust_inherited_attrs
+ *	  Copy the specified query or expression and translate Vars referring
+ *	  to old_rt_index to refer to new_rt_index.
+ *
+ * We also adjust varattno to match the new table by column name, rather
+ * than column number.  This hack makes it possible for child tables to have
+ * different column positions for the "same" attribute as a parent, which
+ * helps ALTER TABLE ADD COLUMN.  Unfortunately this isn't nearly enough to
+ * make it work transparently; there are other places where things fall down
+ * if children and parents don't have the same column numbers for inherited
+ * attributes.  It'd be better to rip this code out and fix ALTER TABLE...
+ */
+Node *
+adjust_inherited_attrs(Node *node,
+					   Index old_rt_index, Oid old_relid,
+					   Index new_rt_index, Oid new_relid)
+{
+	adjust_inherited_attrs_context context;
+
+	/* Handle simple case simply... */
+	if (old_rt_index == new_rt_index)
+	{
+		Assert(old_relid == new_relid);
+		return copyObject(node);
 	}
 
-	return false;
-}
-
-/*
- * new_rangetable_entry -
- *		Replaces the name and relid of 'old_entry' with the values for
- *		'new_relid'.
- *
- *		Returns a copy of 'old_entry' with the parameters substituted.
- */
-static RangeTblEntry *
-new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry)
-{
-	RangeTblEntry *new_entry = copyObject(old_entry);
-
-	/* Replace relation real name and OID, but not the reference name */
-	new_entry->relname = get_rel_name(new_relid);
-	new_entry->relid = new_relid;
-	return new_entry;
-}
-
-/*
- * fix_parsetree_attnums
- *	  Replaces attribute numbers from the relation represented by
- *	  'old_relid' in 'parsetree' with the attribute numbers from
- *	  'new_relid'.
- *
- * The parsetree is MODIFIED IN PLACE.	This is OK only because
- * plan_inherit_queries made a copy of the tree for us to hack upon.
- */
-static void
-fix_parsetree_attnums(Index rt_index,
-					  Oid old_relid,
-					  Oid new_relid,
-					  Query *parsetree)
-{
-	fix_parsetree_attnums_context context;
-
-	if (old_relid == new_relid)
-		return;					/* no work needed for parent rel itself */
-
-	context.rt_index = rt_index;
+	context.old_rt_index = old_rt_index;
+	context.new_rt_index = new_rt_index;
 	context.old_relid = old_relid;
 	context.new_relid = new_relid;
-	context.sublevels_up = 0;
 
-	query_tree_walker(parsetree,
-					  fix_parsetree_attnums_walker,
-					  (void *) &context,
-					  true);
+	/*
+	 * Must be prepared to start with a Query or a bare expression tree.
+	 */
+	if (node && IsA(node, Query))
+	{
+		Query	   *query = (Query *) node;
+		Query	   *newnode;
+
+		FLATCOPY(newnode, query, Query);
+		if (newnode->resultRelation == old_rt_index)
+			newnode->resultRelation = new_rt_index;
+		query_tree_mutator(newnode, adjust_inherited_attrs_mutator,
+						   (void *) &context, false);
+		return (Node *) newnode;
+	}
+	else
+		return adjust_inherited_attrs_mutator(node, &context);
 }
 
-/*
- * Adjust varnos for child tables.	This routine makes it possible for
- * child tables to have different column positions for the "same" attribute
- * as a parent, which helps ALTER TABLE ADD COLUMN.  Unfortunately this isn't
- * nearly enough to make it work transparently; there are other places where
- * things fall down if children and parents don't have the same column numbers
- * for inherited attributes.  It'd be better to rip this code out and fix
- * ALTER TABLE...
- */
-static bool
-fix_parsetree_attnums_walker(Node *node,
-							 fix_parsetree_attnums_context *context)
+static Node *
+adjust_inherited_attrs_mutator(Node *node,
+							   adjust_inherited_attrs_context *context)
 {
 	if (node == NULL)
-		return false;
+		return NULL;
 	if (IsA(node, Var))
 	{
-		Var		   *var = (Var *) node;
+		Var		   *var = (Var *) copyObject(node);
 
-		if (var->varlevelsup == context->sublevels_up &&
-			var->varno == context->rt_index &&
-			var->varattno > 0)
+		if (var->varlevelsup == 0 &&
+			var->varno == context->old_rt_index)
 		{
-			var->varattno = get_attnum(context->new_relid,
-									   get_attname(context->old_relid,
-												   var->varattno));
+			var->varno = context->new_rt_index;
+			if (var->varattno > 0)
+				var->varattno = get_attnum(context->new_relid,
+										   get_attname(context->old_relid,
+													   var->varattno));
 		}
-		return false;
+		return (Node *) var;
 	}
-	if (IsA(node, Query))
+	if (IsA(node, RangeTblRef))
 	{
-		/* Recurse into subselects */
-		bool		result;
+		RangeTblRef	   *rtr = (RangeTblRef *) copyObject(node);
 
-		context->sublevels_up++;
-		result = query_tree_walker((Query *) node,
-								   fix_parsetree_attnums_walker,
-								   (void *) context,
-								   true);
-		context->sublevels_up--;
-		return result;
+		if (rtr->rtindex == context->old_rt_index)
+			rtr->rtindex = context->new_rt_index;
+		return (Node *) rtr;
 	}
-	return expression_tree_walker(node, fix_parsetree_attnums_walker,
+	/*
+	 * We have to process RestrictInfo nodes specially: we do NOT want to
+	 * copy the original subclauseindices list, since the new rel may have
+	 * different indices.  The list will be rebuilt during planning anyway.
+	 */
+	if (IsA(node, RestrictInfo))
+	{
+		RestrictInfo   *oldinfo = (RestrictInfo *) node;
+		RestrictInfo   *newinfo = makeNode(RestrictInfo);
+
+		/* Copy all flat-copiable fields */
+		memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
+
+		newinfo->clause = (Expr *)
+			adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context);
+
+		newinfo->subclauseindices = NIL;
+
+		return (Node *) newinfo;
+	}
+	/*
+	 * NOTE: we do not need to recurse into sublinks, because they should
+	 * already have been converted to subplans before we see them.
+	 */
+	return expression_tree_mutator(node, adjust_inherited_attrs_mutator,
 								  (void *) context);
 }
-
-static Append *
-make_append(List *appendplans,
-			Index rt_index,
-			List *inheritrtable,
-			List *tlist)
-{
-	Append	   *node = makeNode(Append);
-	List	   *subnode;
-
-	node->appendplans = appendplans;
-	node->inheritrelid = rt_index;
-	node->inheritrtable = inheritrtable;
-	node->plan.startup_cost = 0;
-	node->plan.total_cost = 0;
-	node->plan.plan_rows = 0;
-	node->plan.plan_width = 0;
-	foreach(subnode, appendplans)
-	{
-		Plan	   *subplan = (Plan *) lfirst(subnode);
-
-		if (subnode == appendplans)		/* first node? */
-			node->plan.startup_cost = subplan->startup_cost;
-		node->plan.total_cost += subplan->total_cost;
-		node->plan.plan_rows += subplan->plan_rows;
-		if (node->plan.plan_width < subplan->plan_width)
-			node->plan.plan_width = subplan->plan_width;
-	}
-	node->plan.state = (EState *) NULL;
-	node->plan.targetlist = tlist;
-	node->plan.qual = NIL;
-	node->plan.lefttree = (Plan *) NULL;
-	node->plan.righttree = (Plan *) NULL;
-
-	return node;
-}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 36843f834da..b51ec089347 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.67 2000/10/05 19:48:27 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.68 2000/11/12 00:36:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -390,6 +390,37 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
 	return pathnode;
 }
 
+/*
+ * create_append_path
+ *	  Creates a path corresponding to an Append plan, returning the
+ *	  pathnode.
+ *
+ */
+AppendPath *
+create_append_path(RelOptInfo *rel, List *subpaths)
+{
+	AppendPath *pathnode = makeNode(AppendPath);
+	List	   *l;
+
+	pathnode->path.pathtype = T_Append;
+	pathnode->path.parent = rel;
+	pathnode->path.pathkeys = NIL; /* result is always considered unsorted */
+	pathnode->subpaths = subpaths;
+
+	pathnode->path.startup_cost = 0;
+	pathnode->path.total_cost = 0;
+	foreach(l, subpaths)
+	{
+		Path   *subpath = (Path *) lfirst(l);
+
+		if (l == subpaths)		/* first node? */
+			pathnode->path.startup_cost = subpath->startup_cost;
+		pathnode->path.total_cost += subpath->total_cost;
+	}
+
+	return pathnode;
+}
+
 /*
  * create_subqueryscan_path
  *	  Creates a path corresponding to a sequential scan of a subquery,
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 86258b0f644..64720944ce3 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.29 2000/09/29 18:21:23 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.30 2000/11/12 00:36:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,7 +45,6 @@ get_base_rel(Query *root, int relid)
 {
 	List	   *baserels;
 	RelOptInfo *rel;
-	Oid			relationObjectId;
 
 	foreach(baserels, root->base_rel_list)
 	{
@@ -60,7 +59,30 @@ get_base_rel(Query *root, int relid)
 	}
 
 	/* No existing RelOptInfo for this base rel, so make a new one */
-	rel = makeNode(RelOptInfo);
+	rel = make_base_rel(root, relid);
+
+	/* and add it to the list */
+	root->base_rel_list = lcons(rel, root->base_rel_list);
+
+	return rel;
+}
+
+/*
+ * make_base_rel
+ *	  Construct a base-relation RelOptInfo for the specified rangetable index.
+ *
+ * This is split out of get_base_rel so that inheritance-tree processing can
+ * construct baserel nodes for child tables.  We need a RelOptInfo so we can
+ * plan a suitable access path for each child table, but we do NOT want to
+ * enter the child nodes into base_rel_list.  In most contexts, get_base_rel
+ * should be called instead.
+ */
+RelOptInfo *
+make_base_rel(Query *root, int relid)
+{
+	RelOptInfo *rel = makeNode(RelOptInfo);
+	Oid			relationObjectId;
+
 	rel->relids = makeListi1(relid);
 	rel->rows = 0;
 	rel->width = 0;
@@ -95,8 +117,6 @@ get_base_rel(Query *root, int relid)
 		rel->issubquery = true;
 	}
 
-	root->base_rel_list = lcons(rel, root->base_rel_list);
-
 	return rel;
 }
 
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 60521d13475..cbd19f0aae4 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.71 2000/11/08 22:09:58 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.72 2000/11/12 00:37:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -151,8 +151,12 @@ setTargetTable(ParseState *pstate, char *relname, bool inh, bool inJoinSet)
 		/*
 		 * Since the rel was in the rangetable already, it's being read
 		 * as well as written.  Therefore, leave checkForRead true.
+		 *
+		 * Force inh to the desired setting for the target (XXX is this
+		 * reasonable?  It's *necessary* that INSERT target not be marked
+		 * inheritable, but otherwise not too clear what to do if conflict?)
 		 */
-		/* XXX what if pre-existing entry has wrong inh setting? */
+		rte->inh = inh;
 	}
 
 	/* Mark target table as requiring write access. */
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 62848d77348..248baa3f894 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.39 2000/10/26 21:37:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.40 2000/11/12 00:37:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -68,7 +68,12 @@ CreateExecutorState(void)
 	state->es_direction = ForwardScanDirection;
 	state->es_range_table = NIL;
 
+	state->es_result_relations = NULL;
+	state->es_num_result_relations = 0;
 	state->es_result_relation_info = NULL;
+
+	state->es_junkFilter = NULL;
+
 	state->es_into_relation_descriptor = NULL;
 
 	state->es_param_list_info = NULL;
@@ -76,8 +81,6 @@ CreateExecutorState(void)
 
 	state->es_tupleTable = NULL;
 
-	state->es_junkFilter = NULL;
-
 	state->es_query_cxt = CurrentMemoryContext;
 
 	state->es_per_tuple_exprcontext = NULL;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 3deef21855f..67a2beb77c3 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.58 2000/11/11 19:55:33 thomas Exp $
+ * $Id: catversion.h,v 1.59 2000/11/12 00:37:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,7 +52,7 @@
  * catalog changes on the same day...)
  */
 
-/*							yyyymmddN */
-#define CATALOG_VERSION_NO	200011110
+/*                        	yyyymmddN */
+#define CATALOG_VERSION_NO	200011112
 
 #endif
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 9fe59b031a7..2197af567fd 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.52 2000/10/26 21:38:03 tgl Exp $
+ * $Id: executor.h,v 1.53 2000/11/12 00:37:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,7 +56,7 @@ extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
 extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate,
 								   int feature, long count);
 extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
-extern void ExecConstraints(char *caller, Relation rel,
+extern void ExecConstraints(char *caller, ResultRelInfo *resultRelInfo,
 							TupleTableSlot *slot, EState *estate);
 extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
 									ItemPointer tid);
@@ -153,8 +153,8 @@ extern void FreeExprContext(ExprContext *econtext);
 #define ResetExprContext(econtext) \
 	MemoryContextReset((econtext)->ecxt_per_tuple_memory)
 
-extern void ExecOpenIndices(RelationInfo *resultRelationInfo);
-extern void ExecCloseIndices(RelationInfo *resultRelationInfo);
+extern void ExecOpenIndices(ResultRelInfo *resultRelInfo);
+extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
 extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
 					  EState *estate, bool is_update);
 
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index a8edfae6740..8aa0ba39ecd 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tuptable.h,v 1.15 2000/01/26 05:58:06 momjian Exp $
+ * $Id: tuptable.h,v 1.16 2000/11/12 00:37:01 tgl Exp $
  *
  * NOTES
  *	  The tuple table interface is getting pretty ugly.
@@ -56,7 +56,6 @@ typedef struct TupleTableSlot
 	bool		ttc_descIsNew;
 	TupleDesc	ttc_tupleDescriptor;
 	Buffer		ttc_buffer;
-	int			ttc_whichplan;
 } TupleTableSlot;
 
 /* ----------------
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 14cd94baa07..4ba3a70c1b5 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.52 2000/10/26 21:38:12 tgl Exp $
+ * $Id: execnodes.h,v 1.53 2000/11/12 00:37:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,31 +52,6 @@ typedef struct IndexInfo
 	bool		ii_Unique;
 } IndexInfo;
 
-/* ----------------
- *	  RelationInfo information
- *
- *		whenever we update an existing relation, we have to
- *		update indices on the relation.  The RelationInfo class
- *		is used to hold all the information on result relations,
- *		including indices.. -cim 10/15/89
- *
- *		RangeTableIndex			result relation's range table index
- *		RelationDesc			relation descriptor for result relation
- *		NumIndices				number indices existing on result relation
- *		IndexRelationDescs		array of relation descriptors for indices
- *		IndexRelationInfo		array of key/attr info for indices
- * ----------------
- */
-typedef struct RelationInfo
-{
-	NodeTag		type;
-	Index		ri_RangeTableIndex;
-	Relation	ri_RelationDesc;
-	int			ri_NumIndices;
-	RelationPtr ri_IndexRelationDescs;
-	IndexInfo **ri_IndexRelationInfo;
-} RelationInfo;
-
 /* ----------------
  *	  ExprContext
  *
@@ -116,8 +91,6 @@ typedef struct ExprContext
 	/* Values to substitute for Aggref nodes in expression */
 	Datum	   *ecxt_aggvalues; /* precomputed values for Aggref nodes */
 	bool	   *ecxt_aggnulls;	/* null flags for Aggref nodes */
-	/* Range table that Vars in expression refer to --- seldom needed */
-	List	   *ecxt_range_table;
 } ExprContext;
 
 /*
@@ -210,6 +183,35 @@ typedef struct JunkFilter
 	AttrNumber *jf_cleanMap;
 } JunkFilter;
 
+/* ----------------
+ *	  ResultRelInfo information
+ *
+ *		whenever we update an existing relation, we have to
+ *		update indices on the relation.  The ResultRelInfo class
+ *		is used to hold all the information on result relations,
+ *		including indices.. -cim 10/15/89
+ *
+ *		RangeTableIndex			result relation's range table index
+ *		RelationDesc			relation descriptor for result relation
+ *		NumIndices				# of indices existing on result relation
+ *		IndexRelationDescs		array of relation descriptors for indices
+ *		IndexRelationInfo		array of key/attr info for indices
+ *		ConstraintExprs			array of constraint-checking expressions
+ *		junkFilter				for removing junk attributes from tuples
+ * ----------------
+ */
+typedef struct ResultRelInfo
+{
+	NodeTag		type;
+	Index		ri_RangeTableIndex;
+	Relation	ri_RelationDesc;
+	int			ri_NumIndices;
+	RelationPtr ri_IndexRelationDescs;
+	IndexInfo **ri_IndexRelationInfo;
+	List	  **ri_ConstraintExprs;
+	JunkFilter *ri_junkFilter;
+} ResultRelInfo;
+
 /* ----------------
  *	  EState information
  *
@@ -217,7 +219,7 @@ typedef struct JunkFilter
  *
  *		range_table						array of scan relation information
  *
- *		result_relation_information		for update queries
+ *		result_relation information		for insert/update/delete queries
  *
  *		into_relation_descriptor		relation being retrieved "into"
  *
@@ -227,10 +229,6 @@ typedef struct JunkFilter
  *		tupleTable						this is a pointer to an array
  *										of pointers to tuples used by
  *										the executor at any given moment.
- *
- *		junkFilter						contains information used to
- *										extract junk attributes from a tuple.
- *										(see JunkFilter above)
  * ----------------
  */
 typedef struct EState
@@ -239,23 +237,24 @@ typedef struct EState
 	ScanDirection es_direction;
 	Snapshot	es_snapshot;
 	List	   *es_range_table;
-	RelationInfo *es_result_relation_info;
+	ResultRelInfo *es_result_relations;		/* array of ResultRelInfos */
+	int			es_num_result_relations;	/* length of array */
+	ResultRelInfo *es_result_relation_info;	/* currently active array elt */
+	JunkFilter *es_junkFilter;				/* currently active junk filter */
 	Relation	es_into_relation_descriptor;
 	ParamListInfo es_param_list_info;
 	ParamExecData *es_param_exec_vals;	/* this is for subselects */
 	TupleTable	es_tupleTable;
-	JunkFilter *es_junkFilter;
 	uint32		es_processed;	/* # of tuples processed */
 	Oid			es_lastoid;		/* last oid processed (by INSERT) */
 	List	   *es_rowMark;		/* not good place, but there is no other */
 	MemoryContext es_query_cxt;	/* per-query context in which EState lives */
-	/* this ExprContext is for per-output-tuple operations, such as
+	/*
+	 * this ExprContext is for per-output-tuple operations, such as
 	 * constraint checks and index-value computations.  It can be reset
 	 * for each output tuple.  Note that it will be created only if needed.
 	 */
 	ExprContext *es_per_tuple_exprcontext;
-	/* this field is storage space for ExecConstraints(): */
-	List	  **es_result_relation_constraints;
 	/* Below is to re-evaluate plan qual in READ COMMITTED mode */
 	struct Plan *es_origPlan;
 	Pointer		es_evalPlanQual;
@@ -341,15 +340,9 @@ typedef struct ResultState
 /* ----------------
  *	 AppendState information
  *
- *		append nodes have this field "unionplans" which is this
- *		list of plans to execute in sequence..	these variables
- *		keep track of things..
- *
- *		whichplan		which plan is being executed
+ *		whichplan		which plan is being executed (0 .. n-1)
  *		nplans			how many plans are in the list
  *		initialized		array of ExecInitNode() results
- *		result_relation_info_list  array of each subplan's result relation info
- *		junkFilter_list  array of each subplan's junk filter
  * ----------------
  */
 typedef struct AppendState
@@ -358,8 +351,6 @@ typedef struct AppendState
 	int			as_whichplan;
 	int			as_nplans;
 	bool	   *as_initialized;
-	List	   *as_result_relation_info_list;
-	List	   *as_junkFilter_list;
 } AppendState;
 
 /* ----------------------------------------------------------------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 727db39d827..6b64961525a 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.82 2000/11/05 22:50:21 vadim Exp $
+ * $Id: nodes.h,v 1.83 2000/11/12 00:37:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -83,11 +83,12 @@ typedef enum NodeTag
 	T_NestPath,
 	T_MergePath,
 	T_HashPath,
+	T_TidPath,
+	T_AppendPath,
 	T_PathKeyItem,
 	T_RestrictInfo,
 	T_JoinInfo,
 	T_Stream,
-	T_TidPath,
 	T_IndexOptInfo,
 
 	/*---------------------
@@ -95,7 +96,7 @@ typedef enum NodeTag
 	 *---------------------
 	 */
 	T_IndexInfo = 300,
-	T_RelationInfo,
+	T_ResultRelInfo,
 	T_TupleCount,
 	T_TupleTableSlot,
 	T_ExprContext,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 269fa9592be..469230e1c62 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.119 2000/11/05 22:50:21 vadim Exp $
+ * $Id: parsenodes.h,v 1.120 2000/11/12 00:37:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -70,6 +70,16 @@ typedef struct Query
 	Node	   *setOperations;	/* set-operation tree if this is top level
 								 * of a UNION/INTERSECT/EXCEPT query */
 
+	/*
+	 * If the resultRelation turns out to be the parent of an inheritance
+	 * tree, the planner will add all the child tables to the rtable and
+	 * store a list of the rtindexes of all the result relations here.
+	 * This is done at plan time, not parse time, since we don't want to
+	 * commit to the exact set of child tables at parse time.  This field
+	 * ought to go in some sort of TopPlan plan node, not in the Query.
+	 */
+	List	   *resultRelations; /* integer list of RT indexes, or NIL */
+
 	/* internal to planner */
 	List	   *base_rel_list;	/* list of base-relation RelOptInfos */
 	List	   *join_rel_list;	/* list of join-relation RelOptInfos */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 177ab73a13b..eecb74caf2f 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: plannodes.h,v 1.45 2000/10/26 21:38:12 tgl Exp $
+ * $Id: plannodes.h,v 1.46 2000/11/12 00:37:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -134,8 +134,10 @@ typedef struct Plan
 
 
 /* ----------------
- *	 result node -
- *		returns tuples from outer plan that satisfy the qualifications
+ *	 Result node -
+ *		If no outer plan, evaluate a variable-free targetlist.
+ *		If outer plan, return tuples from outer plan that satisfy
+ *		given quals (we can also do a level of projection)
  * ----------------
  */
 typedef struct Result
@@ -146,21 +148,21 @@ typedef struct Result
 } Result;
 
 /* ----------------
- *		append node
+ *	 Append node -
+ *		Generate the concatenation of the results of sub-plans.
  *
- * Append nodes can modify the query's rtable during execution.
- * If inheritrelid > 0, then the RTE with index inheritrelid is replaced
- * by the i'th element of inheritrtable to execute the i'th subplan.
- * We assume that this RTE is not used in any other part of the
- * query plan tree, else confusion may result...
+ * Append nodes are sometimes used to switch between several result relations
+ * (when the target of an UPDATE or DELETE is an inheritance set).  Such a
+ * node will have isTarget true.  The Append executor is then responsible
+ * for updating the executor state to point at the correct target relation
+ * whenever it switches subplans.
  * ----------------
  */
 typedef struct Append
 {
 	Plan		plan;
 	List	   *appendplans;
-	Index		inheritrelid;
-	List	   *inheritrtable;
+	bool		isTarget;
 	AppendState *appendstate;
 } Append;
 
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 1c864f2e721..45d53be5f62 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relation.h,v 1.49 2000/09/29 18:21:39 tgl Exp $
+ * $Id: relation.h,v 1.50 2000/11/12 00:37:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -82,6 +82,14 @@ typedef enum CostSelector
  *		upon creation of the RelOptInfo object; they are filled in when
  *		set_base_rel_pathlist processes the object.
  *
+ *		Note: if a base relation is the root of an inheritance tree
+ *		(SELECT FROM foo*) it is still considered a base rel.  We will
+ *		generate a list of candidate Paths for accessing that table itself,
+ *		and also generate baserel RelOptInfo nodes for each child table,
+ *		with their own candidate Path lists.  Then, an AppendPath is built
+ *		from the cheapest Path for each of these tables, and set to be the
+ *		only available Path for the inheritance baserel.
+ *
  *	 * The presence of the remaining fields depends on the restrictions
  *		and joins that the relation participates in:
  *
@@ -313,6 +321,9 @@ typedef struct IndexPath
 	double		rows;			/* estimated number of result tuples */
 } IndexPath;
 
+/*
+ * TidPath represents a scan by TID
+ */
 typedef struct TidPath
 {
 	Path		path;
@@ -320,6 +331,17 @@ typedef struct TidPath
 	Relids		unjoined_relids;/* some rels not yet part of my Path */
 } TidPath;
 
+/*
+ * AppendPath represents an Append plan, ie, successive execution of
+ * several member plans.  Currently it is only used to handle expansion
+ * of inheritance trees.
+ */
+typedef struct AppendPath
+{
+	Path		path;
+	List	   *subpaths;		/* list of component Paths */
+} AppendPath;
+
 /*
  * All join-type paths share these fields.
  */
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 06912f192f6..993003eaa2f 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pathnode.h,v 1.30 2000/10/05 19:48:33 momjian Exp $
+ * $Id: pathnode.h,v 1.31 2000/11/12 00:37:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,6 +32,7 @@ extern IndexPath *create_index_path(Query *root, RelOptInfo *rel,
 				  List *restriction_clauses,
 				  ScanDirection indexscandir);
 extern TidPath *create_tidscan_path(RelOptInfo *rel, List *tideval);
+extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
 extern Path *create_subqueryscan_path(RelOptInfo *rel);
 
 extern NestPath *create_nestloop_path(RelOptInfo *joinrel,
@@ -63,6 +64,7 @@ extern HashPath *create_hashjoin_path(RelOptInfo *joinrel,
  * prototypes for relnode.c
  */
 extern RelOptInfo *get_base_rel(Query *root, int relid);
+extern RelOptInfo *make_base_rel(Query *root, int relid);
 extern RelOptInfo *get_join_rel(Query *root, RelOptInfo *outer_rel,
 			 RelOptInfo *inner_rel,
 			 List **restrictlist_ptr);
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 2d5de645046..f50c1113f06 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: planmain.h,v 1.47 2000/10/26 21:38:24 tgl Exp $
+ * $Id: planmain.h,v 1.48 2000/11/12 00:37:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,7 @@ extern Plan *query_planner(Query *root, List *tlist, double tuple_fraction);
 extern Plan *create_plan(Query *root, Path *best_path);
 extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
 									   Index scanrelid, Plan *subplan);
+extern Append *make_append(List *appendplans, bool isTarget, List *tlist);
 extern Sort *make_sort(List *tlist, Plan *lefttree, int keycount);
 extern Sort *make_sort_from_pathkeys(List *tlist, Plan *lefttree,
 									 List *pathkeys);
@@ -41,7 +42,6 @@ extern Limit *make_limit(List *tlist, Plan *lefttree,
 extern SetOp *make_setop(SetOpCmd cmd, List *tlist, Plan *lefttree,
 						 List *distinctList, AttrNumber flagColIdx);
 extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
-extern void copy_plan_costsize(Plan *dest, Plan *src);
 
 /*
  * prototypes for plan/initsplan.c
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index 3fc0f435dc3..9467b6485bb 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: planner.h,v 1.17 2000/10/05 19:11:37 tgl Exp $
+ * $Id: planner.h,v 1.18 2000/11/12 00:37:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,6 @@
 
 extern Plan *planner(Query *parse);
 extern Plan *subquery_planner(Query *parse, double tuple_fraction);
-extern Plan *union_planner(Query *parse, double tuple_fraction);
 
 extern Plan *make_sortplan(List *tlist, Plan *plannode, List *sortcls);
 
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index dcdf6fb62f6..be1811d5871 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: prep.h,v 1.24 2000/10/05 19:11:37 tgl Exp $
+ * $Id: prep.h,v 1.25 2000/11/12 00:37:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,10 +33,12 @@ extern List *preprocess_targetlist(List *tlist, int command_type,
  * prototypes for prepunion.c
  */
 extern Plan *plan_set_operations(Query *parse);
+
 extern List *find_all_inheritors(Oid parentrel);
-extern bool find_inheritable_rt_entry(List *rangetable,
-									  Index *rt_index, List **inheritors);
-extern Plan *plan_inherit_queries(Query *root, List *tlist,
-								  Index rt_index, List *inheritors);
+extern List *expand_inherted_rtentry(Query *parse, Index rti);
+
+extern Node *adjust_inherited_attrs(Node *node,
+									Index old_rt_index, Oid old_relid,
+									Index new_rt_index, Oid new_relid);
 
 #endif	 /* PREP_H */