diff -durpN pgsql.orig/src/backend/executor/nodeAppend.c pgsql/src/backend/executor/nodeAppend.c
--- pgsql.orig/src/backend/executor/nodeAppend.c	2010-07-13 10:51:01.000000000 +0200
+++ pgsql/src/backend/executor/nodeAppend.c	2010-09-03 10:50:49.000000000 +0200
@@ -59,9 +59,25 @@
 
 #include "executor/execdebug.h"
 #include "executor/nodeAppend.h"
+#include "utils/lsyscache.h"
+#include "access/nbtree.h"
+
+/* It gets quite confusing having a heap array (indexed by integers) which
+ * contains integers which index into the slots array. These typedefs try to
+ * clear it up but without making simple inline accessing functions they don't
+ * actually produce any warnings on mistakes */
+
+typedef int SlotNumber;
+typedef int HeapPosition;
+
+#define WHICHPLAN_PLANS_UNINITIALIZED (-1)
 
 static bool exec_append_initialize_next(AppendState *appendstate);
 
+static int heap_compare_slots(AppendState *node, SlotNumber slot1, SlotNumber slot2);
+
+static void heap_siftup_slot(AppendState *node);
+static void heap_insert_slot(AppendState *node, SlotNumber new_slot);
 
 /* ----------------------------------------------------------------
  *		exec_append_initialize_next
@@ -142,6 +158,7 @@ ExecInitAppend(Append *node, EState *est
 	appendstate->ps.state = estate;
 	appendstate->appendplans = appendplanstates;
 	appendstate->as_nplans = nplans;
+	appendstate->as_is_ordered = node->isOrdered;
 
 	/*
 	 * Miscellaneous initialization
@@ -175,11 +192,53 @@ ExecInitAppend(Append *node, EState *est
 	ExecAssignResultTypeFromTL(&appendstate->ps);
 	appendstate->ps.ps_ProjInfo = NULL;
 
-	/*
-	 * initialize to scan first subplan
-	 */
-	appendstate->as_whichplan = 0;
-	exec_append_initialize_next(appendstate);
+	if (!appendstate->as_is_ordered)
+	{
+		/*
+		 * initialize to scan first subplan
+		 */
+		appendstate->as_whichplan = 0;
+		exec_append_initialize_next(appendstate);
+	}
+	else
+	{
+		/* set up scan keys and initialize *all* the subnodes */
+		int i;
+
+		appendstate->as_nkeys = node->numCols;
+		appendstate->as_scankeys = palloc(sizeof(ScanKeyData) *  node->numCols);
+		appendstate->as_slots = palloc(sizeof(TupleTableSlot *) * nplans);
+		appendstate->as_heap = palloc(sizeof(int) * nplans);
+		appendstate->as_heap_size = 0;
+
+		for (i=0; i < nplans; i++)
+		{
+			appendstate->as_whichplan = i;
+			exec_append_initialize_next(appendstate);
+		}
+
+		appendstate->as_whichplan = WHICHPLAN_PLANS_UNINITIALIZED;
+
+		for (i = 0; i < node->numCols; i++)
+		{
+			Oid sortFunction;
+			bool reverse;
+
+			get_compare_function_for_ordering_op(node->sortOperators[i],
+												 &sortFunction, &reverse);
+
+			ScanKeyInit(&appendstate->as_scankeys[i],
+						node->sortColIdx[i],
+						InvalidStrategy,
+						sortFunction,
+						(Datum)0);
+
+			if (reverse)
+				appendstate->as_scankeys[i].sk_flags |= SK_BT_DESC;
+			if (node->nullsFirst[i])
+				appendstate->as_scankeys[i].sk_flags |= SK_BT_NULLS_FIRST;
+		}
+	}
 
 	return appendstate;
 }
@@ -193,45 +252,169 @@ ExecInitAppend(Append *node, EState *est
 TupleTableSlot *
 ExecAppend(AppendState *node)
 {
-	for (;;)
+	if (!node->as_is_ordered)
 	{
-		PlanState  *subnode;
-		TupleTableSlot *result;
+		for (;;)
+		{
+			PlanState  *subnode;
+			TupleTableSlot *result;
 
-		/*
-		 * figure out which subplan we are currently processing
-		 */
-		subnode = node->appendplans[node->as_whichplan];
+			/*
+			 * figure out which subplan we are currently processing
+			 */
+			subnode = node->appendplans[node->as_whichplan];
 
-		/*
-		 * get a tuple from the subplan
-		 */
-		result = ExecProcNode(subnode);
+			/*
+			 * get a tuple from the subplan
+			 */
+			result = ExecProcNode(subnode);
+
+			if (!TupIsNull(result))
+			{
+				/*
+				 * If the subplan gave us something then return it as-is. We do
+				 * NOT make use of the result slot that was set up in
+				 * ExecInitAppend; there's no need for it.
+				 */
+				return result;
+			}
 
-		if (!TupIsNull(result))
-		{
 			/*
-			 * If the subplan gave us something then return it as-is. We do
-			 * NOT make use of the result slot that was set up in
-			 * ExecInitAppend; there's no need for it.
+			 * Go on to the "next" subplan in the appropriate direction. If no
+			 * more subplans, return the empty slot set up for us by
+			 * ExecInitAppend.
 			 */
-			return result;
+			if (ScanDirectionIsForward(node->ps.state->es_direction))
+				node->as_whichplan++;
+			else
+				node->as_whichplan--;
+			if (!exec_append_initialize_next(node))
+				return ExecClearTuple(node->ps.ps_ResultTupleSlot);
+
+			/* Else loop back and try to get a tuple from the new subplan */
 		}
+	}
+	else
+	{
+		TupleTableSlot *result;
+		SlotNumber i;
 
-		/*
-		 * Go on to the "next" subplan in the appropriate direction. If no
-		 * more subplans, return the empty slot set up for us by
-		 * ExecInitAppend.
-		 */
-		if (ScanDirectionIsForward(node->ps.state->es_direction))
-			node->as_whichplan++;
-		else
-			node->as_whichplan--;
-		if (!exec_append_initialize_next(node))
-			return ExecClearTuple(node->ps.ps_ResultTupleSlot);
+		if (node->as_whichplan == WHICHPLAN_PLANS_UNINITIALIZED)
+		{
+			for (i = 0; i < node->as_nplans; i++)
+			{
+				node->as_slots[i] = ExecProcNode(node->appendplans[i]);
+				if (!TupIsNull(node->as_slots[i]))
+					heap_insert_slot(node, i);
+			}
+                }
+                else
+                {
+                	i = node->as_whichplan;
+                	node->as_slots[i] = ExecProcNode(node->appendplans[i]);
+                	if (TupIsNull(node->as_slots[i]))
+                		;
+			else if (node->as_heap_size <= 0 || heap_compare_slots(node, node->as_heap[0], i) >= 0)
+				return node->as_slots[i];
+			else 
+				heap_insert_slot(node, i);
+                }
 
-		/* Else loop back and try to get a tuple from the new subplan */
+                if (node->as_heap_size > 0)
+                {
+                	i = node->as_heap[0];
+                	node->as_whichplan = i;
+                	heap_siftup_slot(node);
+                	result = node->as_slots[i];
+		} else {
+			result = ExecClearTuple(node->ps.ps_ResultTupleSlot);
+		}
+
+		return result;
+	}
+}
+
+static void
+heap_insert_slot(AppendState *node, SlotNumber new_slot)
+{
+	HeapPosition j;
+	SlotNumber *heap;
+
+	Assert(!TupIsNull(node->as_slots[new_slot]));
+
+	j = node->as_heap_size++;
+	heap = node->as_heap;
+
+	while (j > 0)
+	{
+		int i = (j-1)/2;
+		if (heap_compare_slots(node, new_slot, node->as_heap[i]) >= 0)
+			break;
+		heap[j] = heap[i];
+		j = i;
 	}
+	heap[j] = new_slot;
+}
+
+static void
+heap_siftup_slot(AppendState *node)
+{
+	HeapPosition i=0, j, n;
+
+	if (--node->as_heap_size <= 0)
+		return;
+	n = node->as_heap_size;
+
+	for (;;)
+	{
+		j = 2 * i + 1;
+		if (j >= n)
+			break;
+		if (j+1 < n && heap_compare_slots(node, node->as_heap[j], node->as_heap[j+1]) > 0)
+			j++;
+		if (heap_compare_slots(node, node->as_heap[n], node->as_heap[j]) <= 0)
+			break;
+		node->as_heap[i] = node->as_heap[j];
+		i = j;
+	}
+	node->as_heap[i] = node->as_heap[n];
+}
+
+static int
+heap_compare_slots(AppendState *node, SlotNumber slot1, SlotNumber slot2)
+{
+	int nkey;
+	TupleTableSlot *s1, *s2;
+
+	Assert(slot1 < node->as_nplans);
+	Assert(slot2 < node->as_nplans);
+
+	s1 = node->as_slots[slot1];
+	s2 = node->as_slots[slot2];
+	Assert(!TupIsNull(s1));
+	Assert(!TupIsNull(s2));
+
+	for (nkey = 0; nkey < node->as_nkeys; nkey++)
+	{
+		ScanKey scankey = node->as_scankeys + nkey;
+		AttrNumber attno = scankey->sk_attno;
+		Datum d1, d2;
+		bool  n1, n2;
+		int compare;
+
+		d1 = slot_getattr(s1, attno, &n1);
+		d2 = slot_getattr(s2, attno, &n2);
+
+		if (n1 && !n2)
+			return (scankey->sk_flags & SK_BT_NULLS_FIRST) ? -1 :  1;
+		if (!n1 && n2)
+			return (scankey->sk_flags & SK_BT_NULLS_FIRST) ?  1 : -1;
+
+		compare = FunctionCall2(&scankey->sk_func, d1, d2);
+		if (compare != 0)
+			return (scankey->sk_flags & SK_BT_DESC) ? -compare : compare;
+	}
+	return 0;
 }
 
 /* ----------------------------------------------------------------
diff -durpN pgsql.orig/src/backend/optimizer/path/allpaths.c pgsql/src/backend/optimizer/path/allpaths.c
--- pgsql.orig/src/backend/optimizer/path/allpaths.c	2010-03-29 00:59:32.000000000 +0200
+++ pgsql/src/backend/optimizer/path/allpaths.c	2010-09-03 10:55:18.000000000 +0200
@@ -51,7 +51,7 @@ static void set_plain_rel_pathlist(Plann
 					   RangeTblEntry *rte);
 static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 						Index rti, RangeTblEntry *rte);
-static void set_dummy_rel_pathlist(RelOptInfo *rel);
+static void set_dummy_rel_pathlist(PlannerInfo *root, RelOptInfo *rel);
 static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 					  Index rti, RangeTblEntry *rte);
 static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
@@ -222,7 +222,7 @@ set_plain_rel_pathlist(PlannerInfo *root
 	if (rel->reloptkind == RELOPT_BASEREL &&
 		relation_excluded_by_constraints(root, rel, rte))
 	{
-		set_dummy_rel_pathlist(rel);
+		set_dummy_rel_pathlist(root, rel);
 		return;
 	}
 
@@ -267,6 +267,22 @@ set_plain_rel_pathlist(PlannerInfo *root
 	set_cheapest(rel);
 }
 
+
+/* It's possible that the child is itself an appendrel, in which case
+ * we can "cut out the middleman" and just add its child paths to our
+ * own list.  (We don't try to do this earlier because we need to
+ * apply both levels of transformation to the quals.)
+ */
+
+#define LAPPEND_PATH_FLATTEN_APPENDPATHS(subpaths_, childpath_)			\
+	do {																\
+		if (IsA((childpath_), AppendPath))								\
+			(subpaths_) = list_concat((subpaths_),						\
+									 ((AppendPath *) (childpath_))->subpaths); \
+		else															\
+			subpaths_ = lappend(subpaths_, childpath_);					\
+	} while (0)
+
 /*
  * set_append_rel_pathlist
  *	  Build access paths for an "append relation"
@@ -283,12 +299,13 @@ set_append_rel_pathlist(PlannerInfo *roo
 						Index rti, RangeTblEntry *rte)
 {
 	int			parentRTindex = rti;
-	List	   *subpaths = NIL;
+	List	   *best_subpaths = NIL;	/* list of paths */
+	List	   *all_pathkeys = NIL;		/* union of all pathkeys in sub paths */
 	double		parent_rows;
 	double		parent_size;
 	double	   *parent_attrsizes;
 	int			nattrs;
-	ListCell   *l;
+	ListCell   *l, *ll, *lll;
 
 	/*
 	 * Initialize to compute size estimates for whole append relation.
@@ -366,7 +383,7 @@ set_append_rel_pathlist(PlannerInfo *roo
 			 * Restriction reduces to constant FALSE or constant NULL after
 			 * substitution, so this child need not be scanned.
 			 */
-			set_dummy_rel_pathlist(childrel);
+			set_dummy_rel_pathlist(root, childrel);
 			continue;
 		}
 		childquals = make_ands_implicit((Expr *) childqual);
@@ -381,7 +398,7 @@ set_append_rel_pathlist(PlannerInfo *roo
 			 * appendrel.  Mark it with a dummy cheapest-path though, in case
 			 * best_appendrel_indexscan() looks at it later.
 			 */
-			set_dummy_rel_pathlist(childrel);
+			set_dummy_rel_pathlist(root, childrel);
 			continue;
 		}
 
@@ -397,7 +414,9 @@ set_append_rel_pathlist(PlannerInfo *roo
 		 * We have to make child entries in the EquivalenceClass data
 		 * structures as well.
 		 */
+#ifdef FIXME
 		if (rel->has_eclass_joins)
+#endif
 		{
 			add_child_rel_equivalences(root, appinfo, rel, childrel);
 			childrel->has_eclass_joins = true;
@@ -413,21 +432,34 @@ set_append_rel_pathlist(PlannerInfo *roo
 
 		/*
 		 * Compute the child's access paths, and add the cheapest one to the
-		 * Append path we are constructing for the parent.
-		 *
-		 * It's possible that the child is itself an appendrel, in which case
-		 * we can "cut out the middleman" and just add its child paths to our
-		 * own list.  (We don't try to do this earlier because we need to
-		 * apply both levels of transformation to the quals.)
+ 		 * list of cheap paths (best_paths) and the pathkeys of all the paths
+ 		 * to the union, all_pathkeys.
 		 */
 		set_rel_pathlist(root, childrel, childRTindex, childRTE);
 
 		childpath = childrel->cheapest_total_path;
-		if (IsA(childpath, AppendPath))
-			subpaths = list_concat(subpaths,
-								   ((AppendPath *) childpath)->subpaths);
-		else
-			subpaths = lappend(subpaths, childpath);
+ 		LAPPEND_PATH_FLATTEN_APPENDPATHS(best_subpaths, childpath);
+ 
+		/* gather up all the pathkeys of all sub-paths
+		 * FIXME -- this is O(n^2)!
+		 */
+		foreach(ll, childrel->pathlist)
+		{
+			ListCell *lll;
+			Path *childpath = (Path *) lfirst(ll);
+			bool found = false;
+			
+			foreach (lll, all_pathkeys)
+			{
+				List *existing_pathkeys = (List *)lfirst(lll);
+ 
+				if (compare_pathkeys(existing_pathkeys,
+									 childpath->pathkeys) == PATHKEYS_EQUAL)
+					found = true;
+			}
+			if (!found)
+				all_pathkeys = lappend(all_pathkeys, childpath->pathkeys);
+		}
 
 		/*
 		 * Accumulate size information from each child.
@@ -487,10 +519,74 @@ set_append_rel_pathlist(PlannerInfo *roo
 	 * the parent rel.	(Note: this is correct even if we have zero or one
 	 * live subpath due to constraint exclusion.)
 	 */
-	add_path(rel, (Path *) create_append_path(rel, subpaths));
+	if (list_length(best_subpaths) == 1)
+	{
+		/* special case for a singleton append node, just grab the pathlist
+		 * from the child rels and use it directly.
+		 */
+		rel->pathlist = ((Path*) linitial(best_subpaths))->parent->pathlist;
+		set_cheapest(rel);
+		return;
+	}
 
-	/* Select cheapest path (pretty easy in this case...) */
-	set_cheapest(rel);
+	/* First add the unordered append path */
+	add_path(rel, (Path *) create_append_path(root, rel, best_subpaths, NIL));
+
+	/* Now try to add various ordered append paths */
+	foreach (ll, all_pathkeys)
+	{
+		List *pathkeys = (List *)lfirst(ll);
+		List *startup_subpaths = NIL; /* subpaths for minimal startup cost append path */
+		List *total_subpaths = NIL;   /* subpaths for minimal total cost append path */
+
+		/* populate both subpaths lists based on this pathkeys list */
+		foreach(lll, root->append_rel_list)
+		{
+			AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lll);
+			RelOptInfo *childrel;
+			Path	   *childpath;
+
+			/* append_rel_list contains all append rels; ignore others */
+			if (appinfo->parent_relid != parentRTindex)
+				continue;
+
+			childrel = find_base_rel(root, appinfo->child_relid);
+			Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
+
+			/* First the cheapest startup cost */
+			childpath = get_cheapest_path_for_pathkeys(childrel->pathlist, 
+													   pathkeys,
+													   TOTAL_COST);
+
+			/* If we can't find any plans with the right order just add the
+			 * cheapest total plan to both paths, we'll have to sort it
+			 * anyways. Perhaps consider not generating a startup path for such
+			 * pathkeys since it'll be unlikely to be the cheapest startup
+			 * cost. */
+
+			if (!childpath) {
+				childpath = childrel->cheapest_total_path;
+				LAPPEND_PATH_FLATTEN_APPENDPATHS(startup_subpaths, childpath);
+				LAPPEND_PATH_FLATTEN_APPENDPATHS(total_subpaths, childpath);
+				continue;
+			}
+
+			LAPPEND_PATH_FLATTEN_APPENDPATHS(total_subpaths, childpath);
+
+			/* Also do the the cheapest startup cost */
+			childpath = get_cheapest_path_for_pathkeys(childrel->pathlist, 
+													   pathkeys,
+													   STARTUP_COST);
+			Assert(childpath); /* above special case ought to have caught this */
+			LAPPEND_PATH_FLATTEN_APPENDPATHS(startup_subpaths, childpath);
+		}
+
+		add_path(rel, (Path *) create_append_path(root, rel, startup_subpaths, pathkeys));
+		add_path(rel, (Path *) create_append_path(root, rel, total_subpaths, pathkeys));
+	}
+
+	/* Select cheapest path */
+  	set_cheapest(rel);
 }
 
 /*
@@ -501,13 +597,13 @@ set_append_rel_pathlist(PlannerInfo *roo
  * AppendPath with no members (see also IS_DUMMY_PATH macro).
  */
 static void
-set_dummy_rel_pathlist(RelOptInfo *rel)
+set_dummy_rel_pathlist(PlannerInfo *root, RelOptInfo *rel)
 {
 	/* Set dummy size estimates --- we leave attr_widths[] as zeroes */
 	rel->rows = 0;
 	rel->width = 0;
 
-	add_path(rel, (Path *) create_append_path(rel, NIL));
+	add_path(rel, (Path *) create_append_path(root, rel, NIL, NIL));
 
 	/* Select cheapest path (pretty easy in this case...) */
 	set_cheapest(rel);
diff -durpN pgsql.orig/src/backend/optimizer/path/equivclass.c pgsql/src/backend/optimizer/path/equivclass.c
--- pgsql.orig/src/backend/optimizer/path/equivclass.c	2010-02-26 03:00:44.000000000 +0100
+++ pgsql/src/backend/optimizer/path/equivclass.c	2010-09-03 10:20:58.000000000 +0200
@@ -1634,9 +1634,10 @@ add_child_rel_equivalences(PlannerInfo *
 		 * Won't generate joinclauses if const or single-member (the latter
 		 * test covers the volatile case too)
 		 */
+#if FIXME
 		if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
 			continue;
-
+#endif
 		/* No point in searching if parent rel not mentioned in eclass */
 		if (!bms_is_subset(parent_rel->relids, cur_ec->ec_relids))
 			continue;
diff -durpN pgsql.orig/src/backend/optimizer/path/joinpath.c pgsql/src/backend/optimizer/path/joinpath.c
--- pgsql.orig/src/backend/optimizer/path/joinpath.c	2010-04-19 09:42:28.000000000 +0200
+++ pgsql/src/backend/optimizer/path/joinpath.c	2010-09-03 10:20:58.000000000 +0200
@@ -955,7 +955,7 @@ best_appendrel_indexscan(PlannerInfo *ro
 		return NULL;
 
 	/* Form and return the completed Append path. */
-	return (Path *) create_append_path(rel, append_paths);
+	return (Path *) create_append_path(root, rel, append_paths, NIL);
 }
 
 /*
diff -durpN pgsql.orig/src/backend/optimizer/path/joinrels.c pgsql/src/backend/optimizer/path/joinrels.c
--- pgsql.orig/src/backend/optimizer/path/joinrels.c	2010-02-26 03:00:45.000000000 +0100
+++ pgsql/src/backend/optimizer/path/joinrels.c	2010-09-03 10:20:58.000000000 +0200
@@ -932,7 +932,7 @@ mark_dummy_rel(RelOptInfo *rel)
 	rel->pathlist = NIL;
 
 	/* Set up the dummy path */
-	add_path(rel, (Path *) create_append_path(rel, NIL));
+	add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NIL));
 
 	/* Set or update cheapest_total_path */
 	set_cheapest(rel);
diff -durpN pgsql.orig/src/backend/optimizer/plan/createplan.c pgsql/src/backend/optimizer/plan/createplan.c
--- pgsql.orig/src/backend/optimizer/plan/createplan.c	2010-07-13 10:51:03.000000000 +0200
+++ pgsql/src/backend/optimizer/plan/createplan.c	2010-09-03 10:27:46.000000000 +0200
@@ -24,6 +24,7 @@
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
+#include "optimizer/paths.h"
 #include "optimizer/plancat.h"
 #include "optimizer/planmain.h"
 #include "optimizer/predtest.h"
@@ -609,11 +610,19 @@ create_append_plan(PlannerInfo *root, Ap
 	foreach(subpaths, best_path->subpaths)
 	{
 		Path	   *subpath = (Path *) lfirst(subpaths);
+		Plan	   *subplan;
 
-		subplans = lappend(subplans, create_plan_recurse(root, subpath));
+		subplan = create_plan(root, subpath);
+		if (!pathkeys_contained_in(best_path->path.pathkeys, subpath->pathkeys))
+			subplan = (Plan *)make_sort_from_pathkeys(root,
+													  subplan,
+													  best_path->path.pathkeys,
+													  -1.0);
+
+		subplans = lappend(subplans, subplan);
 	}
 
-	plan = make_append(subplans, tlist);
+	plan = make_append(root, subplans, tlist, best_path->path.pathkeys);
 
 	return (Plan *) plan;
 }
@@ -2784,7 +2793,8 @@ make_worktablescan(List *qptlist,
 }
 
 Append *
-make_append(List *appendplans, List *tlist)
+make_append(PlannerInfo *root, List *appendplans,
+				   List *tlist, List *pathkeys)
 {
 	Append	   *node = makeNode(Append);
 	Plan	   *plan = &node->plan;
@@ -2792,9 +2802,11 @@ make_append(List *appendplans, List *tli
 	ListCell   *subnode;
 
 	/*
-	 * Compute cost as sum of subplan costs.  We charge nothing extra for the
-	 * Append itself, which perhaps is too optimistic, but since it doesn't do
-	 * any selection or projection, it is a pretty cheap node.
+	 * Compute cost as sum of subplan costs. We charge nothing extra for a
+	 * plain Append itself, which perhaps is too optimistic, but since it
+	 * doesn't do any selection or projection, it is a pretty cheap node. In
+	 * the case of an ordered append we construct an equivalent bounded Sort
+	 * node and steal the cost calculations from it.
 	 */
 	plan->startup_cost = 0;
 	plan->total_cost = 0;
@@ -2804,8 +2816,10 @@ make_append(List *appendplans, List *tli
 	{
 		Plan	   *subplan = (Plan *) lfirst(subnode);
 
-		if (subnode == list_head(appendplans))	/* first node? */
-			plan->startup_cost = subplan->startup_cost;
+ 		/* If it's ordered then the startup cost is the sum of the startup
+ 		 * costs, otherwise it's the startup cost of just the first plan */
+ 		if (pathkeys || subnode == list_head(appendplans))
+ 			plan->startup_cost += subplan->startup_cost;
 		plan->total_cost += subplan->total_cost;
 		plan->plan_rows += subplan->plan_rows;
 		total_size += subplan->plan_width * subplan->plan_rows;
@@ -2821,6 +2835,30 @@ make_append(List *appendplans, List *tli
 	plan->righttree = NULL;
 	node->appendplans = appendplans;
 
+	if (!pathkeys)
+		node->isOrdered = false;
+	else 
+	{
+		/* generate a throwaway sort node to find the sort functions */
+		Sort *tmp = make_sort_from_pathkeys(root, (Plan*)node, pathkeys, list_length(appendplans));
+		
+		node->isOrdered = true;
+		
+		node->numCols 		= tmp->numCols;
+		node->sortColIdx 	= tmp->sortColIdx;
+		node->sortOperators = tmp->sortOperators;
+		node->nullsFirst 	= tmp->nullsFirst;
+
+		/* a limited sort is the same kind of work (bounded heap sort) as an
+		 * ordered append with the bound set to the number of plans, so we just
+		 * use that to calculate the total cost. The startup cost is just the
+		 * sum of the startup costs of the nodes plus a bit to build the heap
+		 * (similar to processing that many rows in the bounded sort case).
+		 */
+		plan->total_cost = tmp->plan.total_cost - (enable_sort ? 0 : disable_cost);
+		plan->startup_cost += plan->total_cost / plan->plan_rows * list_length(appendplans);
+	}
+
 	return node;
 }
 
@@ -3182,9 +3220,10 @@ make_sort_from_pathkeys(PlannerInfo *roo
 			{
 				EquivalenceMember *em = (EquivalenceMember *) lfirst(j);
 
+#ifdef FIXME
 				if (em->em_is_const || em->em_is_child)
 					continue;
-
+#endif
 				tle = tlist_member((Node *) em->em_expr, tlist);
 				if (tle)
 				{
@@ -3218,8 +3257,10 @@ make_sort_from_pathkeys(PlannerInfo *roo
 					List	   *exprvars;
 					ListCell   *k;
 
+#ifdef FIXME
 					if (em->em_is_const || em->em_is_child)
 						continue;
+#endif
 					sortexpr = em->em_expr;
 					exprvars = pull_var_clause((Node *) sortexpr,
 											   PVC_INCLUDE_PLACEHOLDERS);
diff -durpN pgsql.orig/src/backend/optimizer/prep/prepunion.c pgsql/src/backend/optimizer/prep/prepunion.c
--- pgsql.orig/src/backend/optimizer/prep/prepunion.c	2010-07-11 11:14:51.000000000 +0200
+++ pgsql/src/backend/optimizer/prep/prepunion.c	2010-09-03 10:22:43.000000000 +0200
@@ -449,7 +449,7 @@ generate_union_plan(SetOperationStmt *op
 	/*
 	 * Append the child results together.
 	 */
-	plan = (Plan *) make_append(planlist, tlist);
+	plan = (Plan *) make_append(root, planlist, tlist, NIL);
 
 	/*
 	 * For UNION ALL, we just need the Append plan.  For UNION, need to add
@@ -540,7 +540,7 @@ generate_nonunion_plan(SetOperationStmt 
 	/*
 	 * Append the child results together.
 	 */
-	plan = (Plan *) make_append(planlist, tlist);
+	plan = (Plan *) make_append(root, planlist, tlist, NIL);
 
 	/* Identify the grouping semantics */
 	groupList = generate_setop_grouplist(op, tlist);
diff -durpN pgsql.orig/src/backend/optimizer/util/pathnode.c pgsql/src/backend/optimizer/util/pathnode.c
--- pgsql.orig/src/backend/optimizer/util/pathnode.c	2010-03-29 00:59:33.000000000 +0200
+++ pgsql/src/backend/optimizer/util/pathnode.c	2010-09-03 10:20:58.000000000 +0200
@@ -635,31 +635,74 @@ create_tidscan_path(PlannerInfo *root, R
 /*
  * create_append_path
  *	  Creates a path corresponding to an Append plan, returning the
- *	  pathnode.
+ *	  pathnode. 
+ *
+ *    Note that we must support create_append_path(null, rel, nil, nil) which
+ *    is used to create dummy plans.
  */
+#define LOG2(x)  (log(x) / 0.693147180559945)
+
 AppendPath *
-create_append_path(RelOptInfo *rel, List *subpaths)
+create_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, List *pathkeys)
 {
 	AppendPath *pathnode = makeNode(AppendPath);
 	ListCell   *l;
+	Cost total_cost=0, startup_cost=0;
+	Cost this_total_cost, this_startup_cost;
+	int npaths = 0;
 
 	pathnode->path.pathtype = T_Append;
 	pathnode->path.parent = rel;
-	pathnode->path.pathkeys = NIL;		/* result is always considered
-										 * unsorted */
+	pathnode->path.pathkeys = pathkeys;
 	pathnode->subpaths = subpaths;
 
-	pathnode->path.startup_cost = 0;
-	pathnode->path.total_cost = 0;
 	foreach(l, subpaths)
 	{
 		Path	   *subpath = (Path *) lfirst(l);
 
-		if (l == list_head(subpaths))	/* first node? */
-			pathnode->path.startup_cost = subpath->startup_cost;
-		pathnode->path.total_cost += subpath->total_cost;
+		if (pathkeys_contained_in(pathkeys, subpath->pathkeys))  
+		{
+			this_startup_cost = subpath->startup_cost;
+			this_total_cost   = subpath->total_cost;
+		} else {
+			Path sort_path; /* dummy for result of cost_sort */
+			cost_sort(&sort_path,
+					  root,
+					  pathkeys,
+					  subpath->total_cost, 
+					  subpath->parent->tuples, 
+					  subpath->parent->width, 
+					  -1.0);
+			this_total_cost   = sort_path.total_cost;
+			this_startup_cost = sort_path.startup_cost;
+		}
+		
+		npaths++;
+		total_cost += this_total_cost;
+		/* If it's unsorted the startup cost is just the first subpath's
+		 * startup cost, otherwise it's the sum of all startup costs */
+		if (pathkeys != NIL || l == list_head(subpaths))
+			startup_cost += this_startup_cost;
+	}
+
+	/* The cost of merging using a heapsort */
+	if (pathkeys != NIL)
+	{
+		Path sort_path;
+		cost_sort(&sort_path,
+				  root,
+				  pathkeys,
+				  total_cost,
+				  rel->rows,
+				  rel->width, 
+				  npaths);
+		total_cost = sort_path.total_cost - (enable_sort ? 0 : disable_cost);
+		startup_cost += total_cost / rel->rows * npaths;
 	}
 
+	pathnode->path.total_cost = total_cost;
+	pathnode->path.startup_cost = startup_cost;
+	
 	return pathnode;
 }
 
diff -durpN pgsql.orig/src/include/nodes/execnodes.h pgsql/src/include/nodes/execnodes.h
--- pgsql.orig/src/include/nodes/execnodes.h	2010-07-29 11:33:00.000000000 +0200
+++ pgsql/src/include/nodes/execnodes.h	2010-09-03 10:46:22.000000000 +0200
@@ -1047,6 +1047,13 @@ typedef struct AppendState
 	PlanState **appendplans;	/* array of PlanStates for my inputs */
 	int			as_nplans;
 	int			as_whichplan;
+
+	bool		as_is_ordered;
+	int			as_nkeys;
+	ScanKey		as_scankeys;		/* array of length as_nkeys */
+	TupleTableSlot **as_slots;		/* array of length as_nplans */
+	int			*as_heap;	/* array of length as_nplans */
+	int			as_heap_size;	/* how many slots are present in the heap */
 } AppendState;
 
 /* ----------------
diff -durpN pgsql.orig/src/include/nodes/plannodes.h pgsql/src/include/nodes/plannodes.h
--- pgsql.orig/src/include/nodes/plannodes.h	2010-07-13 10:51:07.000000000 +0200
+++ pgsql/src/include/nodes/plannodes.h	2010-09-03 10:47:41.000000000 +0200
@@ -180,6 +180,14 @@ typedef struct Append
 {
 	Plan		plan;
 	List	   *appendplans;
+	bool		isOrdered;
+
+	/* used only if the append is an ordered append */
+
+	int		numCols;	/* number of sort-key columns */
+	AttrNumber *sortColIdx;		/* their indexes in the target list */
+	Oid	   *sortOperators;	/* OIDs of operators to sort them by */
+	bool	   *nullsFirst;		/* NULLS FIRST/LAST directions */
 } Append;
 
 /* ----------------
diff -durpN pgsql.orig/src/include/optimizer/pathnode.h pgsql/src/include/optimizer/pathnode.h
--- pgsql.orig/src/include/optimizer/pathnode.h	2010-03-29 00:59:33.000000000 +0200
+++ pgsql/src/include/optimizer/pathnode.h	2010-09-03 10:20:58.000000000 +0200
@@ -46,7 +46,10 @@ extern BitmapOrPath *create_bitmap_or_pa
 					  List *bitmapquals);
 extern TidPath *create_tidscan_path(PlannerInfo *root, RelOptInfo *rel,
 					List *tidquals);
-extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
+extern AppendPath *create_append_path(PlannerInfo *root,
+									  RelOptInfo *rel, 
+									  List *subpaths,
+									  List *pathkeys);
 extern ResultPath *create_result_path(List *quals);
 extern MaterialPath *create_material_path(RelOptInfo *rel, Path *subpath);
 extern UniquePath *create_unique_path(PlannerInfo *root, RelOptInfo *rel,
diff -durpN pgsql.orig/src/include/optimizer/planmain.h pgsql/src/include/optimizer/planmain.h
--- pgsql.orig/src/include/optimizer/planmain.h	2010-03-29 00:59:33.000000000 +0200
+++ pgsql/src/include/optimizer/planmain.h	2010-09-03 10:44:50.000000000 +0200
@@ -43,7 +43,8 @@ extern Node *fix_indexqual_operand(Node 
 extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
 				  Index scanrelid, Plan *subplan,
 				  List *subrtable, List *subrowmark);
-extern Append *make_append(List *appendplans, List *tlist);
+extern Append *make_append(PlannerInfo *root, List *appendplans,
+					 List *tlist, List *pathkeys);
 extern RecursiveUnion *make_recursive_union(List *tlist,
 					 Plan *lefttree, Plan *righttree, int wtParam,
 					 List *distinctList, long numGroups);
