Thank you for pointing out. I missed the warning.

> There is a new compiler warning:
> 
> setrefs.c: In function ‘set_plan_refs’:
> setrefs.c:782:7: warning: initialization from incompatible pointer type
> [enabled by default]

Added explicit cast there and rebased to current master.
Checked no new warning by this patch.
make check succeeded at both $(src) and $(src)/src/test.

regards,

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index d8aa35d..86abdf6 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1063,15 +1063,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 		List	   *set_sortclauses;
 
 		/*
-		 * If there's a top-level ORDER BY, assume we have to fetch all the
-		 * tuples.	This might be too simplistic given all the hackery below
-		 * to possibly avoid the sort; but the odds of accurate estimates here
-		 * are pretty low anyway.
-		 */
-		if (parse->sortClause)
-			tuple_fraction = 0.0;
-
-		/*
 		 * Construct the plan for set operations.  The result will not need
 		 * any work except perhaps a top-level sort and/or LIMIT.  Note that
 		 * any special work for recursive unions is the responsibility of
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index b78d727..c6abe18 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -778,9 +778,26 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 				Assert(splan->plan.qual == NIL);
 				foreach(l, splan->appendplans)
 				{
-					lfirst(l) = set_plan_refs(root,
-											  (Plan *) lfirst(l),
-											  rtoffset);
+					Append *newp =
+						(Append *)set_plan_refs(root,
+												(Plan *) lfirst(l),
+												rtoffset);
+					/*
+					 * UNION on inherited tables may create directly nested
+					 * Appends in plan tree. This structure can be flatten by
+					 * taking grandchildren into parent.
+					 */
+					if (IsA(newp, Append) && 
+						list_length(newp->appendplans) > 0)
+					{
+						ListCell *plc = list_head(newp->appendplans);
+						lfirst(l) = lfirst(plc);
+						for_each_cell(plc, lnext(plc))
+							l = lappend_cell(splan->appendplans,
+											 l, lfirst(plc));
+					}
+					else
+						lfirst(l) = newp;
 				}
 			}
 			break;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index e249628..e8a78a7 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -29,6 +29,7 @@
 #include "postgres.h"
 
 #include <limits.h>
+#include <math.h>
 
 #include "access/heapam.h"
 #include "access/htup_details.h"
@@ -60,6 +61,7 @@ typedef struct
 static Plan *recurse_set_operations(Node *setOp, PlannerInfo *root,
 					   double tuple_fraction,
 					   List *colTypes, List *colCollations,
+					   List *groupClauses,
 					   bool junkOK,
 					   int flag, List *refnames_tlist,
 					   List **sortClauses, double *pNumGroups);
@@ -78,7 +80,8 @@ static Plan *generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,
 static List *recurse_union_children(Node *setOp, PlannerInfo *root,
 					   double tuple_fraction,
 					   SetOperationStmt *top_union,
-					   List *refnames_tlist);
+					   List *refnames_tlist,
+					   List **child_sortclause);
 static Plan *make_union_unique(SetOperationStmt *op, Plan *plan,
 				  PlannerInfo *root, double tuple_fraction,
 				  List **sortClauses);
@@ -97,7 +100,8 @@ static List *generate_append_tlist(List *colTypes, List *colCollations,
 					  bool flag,
 					  List *input_plans,
 					  List *refnames_tlist);
-static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist);
+static List *generate_setop_grouplist(List *groupClauses, List *targetlist,
+									  List *sortClauses);
 static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,
 						 Index rti);
 static void make_inh_translation_list(Relation oldrelation,
@@ -152,6 +156,15 @@ plan_set_operations(PlannerInfo *root, double tuple_fraction,
 	Assert(parse->distinctClause == NIL);
 
 	/*
+	 * If there's a top-level ORDER BY, assume we have to fetch all the tuples
+	 * except for UNION. This might be too simplistic given all the hackery
+	 * below to possibly avoid the sort; but the odds of accurate estimates
+	 * here are pretty low anyway.
+	 */
+	if (parse->sortClause && topop->op != SETOP_UNION)
+		tuple_fraction = 0.0;
+
+	/*
 	 * We'll need to build RelOptInfos for each of the leaf subqueries, which
 	 * are RTE_SUBQUERY rangetable entries in this Query.  Prepare the index
 	 * arrays for that.
@@ -186,18 +199,49 @@ plan_set_operations(PlannerInfo *root, double tuple_fraction,
 	 */
 	return recurse_set_operations((Node *) topop, root, tuple_fraction,
 								  topop->colTypes, topop->colCollations,
+								  topop->groupClauses,
 								  true, -1,
 								  leftmostQuery->targetList,
 								  sortClauses, NULL);
 }
 
 /*
+ * save_plannerglobal
+ *
+ * save planner global to allow multiple calls of subquery_planner at the same
+ * global status. This is done apartly from copyObject so as to do medium
+ * shallow copying.
+ */
+static PlannerGlobal *
+save_plannerglobal(const PlannerGlobal *in)
+{
+	PlannerGlobal *save = makeNode(PlannerGlobal);
+
+	save->boundParams		= in->boundParams;
+	save->subplans			= list_copy(in->subplans);
+	save->subroots			= list_copy(in->subroots);
+	save->rewindPlanIDs		= bms_copy(in->rewindPlanIDs);
+	save->finalrtable		= list_copy(in->finalrtable);
+	save->finalrowmarks		= list_copy(in->finalrowmarks); 
+	save->resultRelations	= list_copy(in->resultRelations);
+	save->relationOids		= list_copy(in->relationOids);
+	save->invalItems		= list_copy(in->invalItems);
+	save->nParamExec		= in->nParamExec;
+	save->lastPHId			= in->lastPHId;
+	save->lastRowMarkId		= in->lastRowMarkId;
+	save->transientPlan		= in->transientPlan;
+
+	return save;
+}
+
+/*
  * recurse_set_operations
  *	  Recursively handle one step in a tree of set operations
  *
  * tuple_fraction: fraction of tuples we expect to retrieve from node
  * colTypes: OID list of set-op's result column datatypes
  * colCollations: OID list of set-op's result column collations
+ * groupClauses: parent setop's grouping clause.
  * junkOK: if true, child resjunk columns may be left in the result
  * flag: if >= 0, add a resjunk output column indicating value of flag
  * refnames_tlist: targetlist to take column names from
@@ -215,6 +259,7 @@ static Plan *
 recurse_set_operations(Node *setOp, PlannerInfo *root,
 					   double tuple_fraction,
 					   List *colTypes, List *colCollations,
+					   List *groupClauses,
 					   bool junkOK,
 					   int flag, List *refnames_tlist,
 					   List **sortClauses, double *pNumGroups)
@@ -223,14 +268,21 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 	{
 		RangeTblRef *rtr = (RangeTblRef *) setOp;
 		RangeTblEntry *rte = root->simple_rte_array[rtr->rtindex];
-		Query	   *subquery = rte->subquery;
+		Query	   *subquery = rte->subquery,
+				   *pdquery;
 		RelOptInfo *rel;
-		PlannerInfo *subroot;
+		PlannerInfo *subroot,
+					*pdsubroot; /* 'pd' comes from 'Pushed Down' */
+		PlannerGlobal *pdglob;
 		Plan	   *subplan,
-				   *plan;
+				   *plan,
+				   *pdplan;
+		bool		try_pushdown = false;
 
 		Assert(subquery != NULL);
 
+		*sortClauses = NIL;
+
 		/*
 		 * We need to build a RelOptInfo for each leaf subquery.  This isn't
 		 * used for anything here, but it carries the subroot data structures
@@ -243,12 +295,125 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 
 		/*
 		 * Generate plan for primitive subquery
+		 *
+		 * Consider whether ORDER BY or groupings of the parent set operation
+		 * can be pushed down onto this subquery.
 		 */
+
+		/*
+		 * Consider whether ORDER BY or groupings of the parent set operation
+		 * can be pushed down onto this subquery.
+		 */
+		if(root->parse->sortClause && !subquery->sortClause &&
+		   tuple_fraction >= 1)
+		{
+			ListCell *lcs, *lcd;
+			int refno = 1;
+
+			/*
+			 * Check if the sort cluase of the root can be applied on this
+			 * subquery. All non-junk tlist items shoud be simple Var and
+			 * their types match and ressortgroupref is ordered in their
+			 * appearance order.
+			 */
+			try_pushdown = true;
+			forboth(lcs, root->parse->targetList, lcd, subquery->targetList)
+			{
+				TargetEntry *ste = (TargetEntry*) lfirst(lcs);
+				TargetEntry *dte = (TargetEntry*) lfirst(lcd);
+				Node *sexpr = (Node*)ste->expr;
+				Node *dexpr = (Node*)dte->expr;
+
+				if (ste->resjunk && dte->resjunk) continue;
+
+				if (ste->resjunk != dte->resjunk ||
+					!IsA(sexpr, Var) || !IsA(dexpr, Var) ||
+					exprType(sexpr) != exprType(dexpr) ||
+					(root->parse->sortClause &&
+					 ste->ressortgroupref != refno++))
+				{
+					try_pushdown = false;
+					break;
+				}
+			}
+		}
+
+		if (try_pushdown)
+		{
+			pdquery = copyObject(subquery);
+
+			if (!pdquery->sortClause)
+			{
+				ListCell *lcs, *lcd;
+
+				pdquery->sortClause = root->parse->sortClause;
+
+				forboth(lcs, root->parse->targetList,
+						lcd, pdquery->targetList)
+				{
+					TargetEntry *ste = (TargetEntry*) lfirst(lcs);
+					TargetEntry *dte = (TargetEntry*) lfirst(lcd);
+					dte->ressortgroupref = ste->ressortgroupref;
+				}
+			}
+			
+			/*
+			 * Pushing down groupings. Set operations doesn't accept
+			 * distinct clauses.
+			 */
+			if (!pdquery->setOperations &&
+				!pdquery->distinctClause && groupClauses)
+
+				pdquery->distinctClause =
+					generate_setop_grouplist(groupClauses,
+											 pdquery->targetList,
+											 pdquery->sortClause);
+
+			if (tuple_fraction >= 1 &&
+				!pdquery->limitCount && !pdquery->limitOffset)
+				pdquery->limitCount =
+					(Node*)makeConst(INT8OID, -1, InvalidOid, sizeof(int64),
+									 Int64GetDatum(tuple_fraction),
+									 false, FLOAT8PASSBYVAL);
+
+			pdglob = save_plannerglobal(root->glob);
+		}
+		
 		subplan = subquery_planner(root->glob, subquery,
 								   root,
 								   false, tuple_fraction,
 								   &subroot);
 
+		if (try_pushdown)
+		{
+			pdplan = subquery_planner(pdglob, pdquery,
+									  root,
+									  false, tuple_fraction,
+									  &pdsubroot);
+
+			if (pdplan->total_cost < subplan->total_cost)
+			{
+				subplan = pdplan;
+				subroot = pdsubroot;
+				/*
+				 * Glob cannot be moved because this is referred to from
+				 * many places by its address. So set the address of the
+				 * original glob to subroot, then copy new values there.
+				 */
+				subroot->glob = root->glob;
+				*root->glob = *pdglob;
+			}
+		}
+
+		/*
+		 * This plan is sorted on sortClause if any, else sorted
+		 * on distinctClause.
+		 */
+		if (subquery->sortClause)
+			*sortClauses = subquery->sortClause;
+		else
+			*sortClauses = subquery->distinctClause;
+
 		/* Save subroot and subplan in RelOptInfo for setrefs.c */
 		rel->subplan = subplan;
 		rel->subroot = subroot;
@@ -290,12 +455,6 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 							  rtr->rtindex,
 							  subplan);
 
-		/*
-		 * We don't bother to determine the subquery's output ordering since
-		 * it won't be reflected in the set-op result anyhow.
-		 */
-		*sortClauses = NIL;
-
 		return plan;
 	}
 	else if (IsA(setOp, SetOperationStmt))
@@ -379,12 +538,14 @@ generate_recursion_plan(SetOperationStmt *setOp, PlannerInfo *root,
 	 */
 	lplan = recurse_set_operations(setOp->larg, root, tuple_fraction,
 								   setOp->colTypes, setOp->colCollations,
+								   setOp->groupClauses,
 								   false, -1,
 								   refnames_tlist, sortClauses, NULL);
 	/* The right plan will want to look at the left one ... */
 	root->non_recursive_plan = lplan;
 	rplan = recurse_set_operations(setOp->rarg, root, tuple_fraction,
 								   setOp->colTypes, setOp->colCollations,
+								   setOp->groupClauses,
 								   false, -1,
 								   refnames_tlist, sortClauses, NULL);
 	root->non_recursive_plan = NULL;
@@ -409,7 +570,8 @@ generate_recursion_plan(SetOperationStmt *setOp, PlannerInfo *root,
 		double		dNumGroups;
 
 		/* Identify the grouping semantics */
-		groupList = generate_setop_grouplist(setOp, tlist);
+		groupList =
+			generate_setop_grouplist(setOp->groupClauses, tlist, NULL);
 
 		/* We only support hashing here */
 		if (!grouping_is_hashable(groupList))
@@ -452,20 +614,7 @@ generate_union_plan(SetOperationStmt *op, PlannerInfo *root,
 	List	   *planlist;
 	List	   *tlist;
 	Plan	   *plan;
-
-	/*
-	 * If plain UNION, tell children to fetch all tuples.
-	 *
-	 * Note: in UNION ALL, we pass the top-level tuple_fraction unmodified to
-	 * each arm of the UNION ALL.  One could make a case for reducing the
-	 * tuple fraction for later arms (discounting by the expected size of the
-	 * earlier arms' results) but it seems not worth the trouble. The normal
-	 * case where tuple_fraction isn't already zero is a LIMIT at top level,
-	 * and passing it down as-is is usually enough to get the desired result
-	 * of preferring fast-start plans.
-	 */
-	if (!op->all)
-		tuple_fraction = 0.0;
+	List	   *lsort, *rsort;
 
 	/*
 	 * If any of my children are identical UNION nodes (same op, all-flag, and
@@ -475,34 +624,99 @@ generate_union_plan(SetOperationStmt *op, PlannerInfo *root,
 	 */
 	planlist = list_concat(recurse_union_children(op->larg, root,
 												  tuple_fraction,
-												  op, refnames_tlist),
+												  op, refnames_tlist,
+												  &lsort),
 						   recurse_union_children(op->rarg, root,
 												  tuple_fraction,
-												  op, refnames_tlist));
+												  op, refnames_tlist,
+												  &rsort));
 
 	/*
-	 * Generate tlist for Append plan node.
+	 * Generate tlist for Append/MergeAppend plan node.
 	 *
-	 * The tlist for an Append plan isn't important as far as the Append is
-	 * concerned, but we must make it look real anyway for the benefit of the
-	 * next plan level up.
+	 * The tlist for an Append/MergeAppend plan isn't important as far as the
+	 * they are concerned, but we must make it look real anyway for the
+	 * benefit of the next plan level up.
 	 */
 	tlist = generate_append_tlist(op->colTypes, op->colCollations, false,
 								  planlist, refnames_tlist);
 
-	/*
-	 * Append the child results together.
-	 */
-	plan = (Plan *) make_append(planlist, tlist);
-
-	/*
-	 * For UNION ALL, we just need the Append plan.  For UNION, need to add
-	 * node(s) to remove duplicates.
-	 */
-	if (op->all)
-		*sortClauses = NIL;		/* result of UNION ALL is always unsorted */
+	if (lsort != NIL && equal(lsort, rsort))
+	{
+		/*
+		 * Generate MergeAppend plan if both children are sorted on the same
+		 * sort clause or groupClauses.
+		 */
+		ListCell *lc, *slc;
+		int i = 0;
+		double total_size;
+		Plan *p;
+		List *distinctClause;
+
+		MergeAppend *node = makeNode(MergeAppend);
+		node->plan.targetlist = tlist;
+		node->plan.qual = NIL;
+		node->plan.lefttree = NULL;
+		node->plan.righttree = NULL;
+		node->mergeplans = planlist;
+		node->numCols = list_length(root->parse->targetList);
+		node->sortColIdx    = (AttrNumber*)palloc(sizeof(AttrNumber) * node->numCols);
+		node->sortOperators = (Oid*)palloc(sizeof(Oid) * node->numCols);
+		node->collations    = (Oid*)palloc(sizeof(Oid) * node->numCols);
+		node->nullsFirst    = (bool*)palloc(sizeof(bool) * node->numCols);
+
+		distinctClause = 
+			generate_setop_grouplist(op->groupClauses,
+									 node->plan.targetlist,
+									 root->parse->sortClause);
+		forboth (slc, distinctClause, lc, node->plan.targetlist)
+		{
+			TargetEntry *te = (TargetEntry*) lfirst(lc);
+			SortGroupClause *sgc = (SortGroupClause *) lfirst(slc);
+
+			Assert(sgc->tleSortGroupRef == te->ressortgroupref);
+			node->sortColIdx[i] = i + 1;
+			node->sortOperators[i] = sgc->sortop;
+			node->collations[i] = exprCollation((Node*)te->expr);
+			node->nullsFirst[i] = sgc->nulls_first;
+			i++;
+		}
+		lc = list_head(node->mergeplans);
+		p = (Plan*) lfirst(lc);
+		node->plan.startup_cost = p->startup_cost;
+		node->plan.total_cost   = p->total_cost;
+		node->plan.plan_rows    = p->plan_rows;
+		total_size             = p->plan_rows * p->plan_width;
+		for_each_cell(lc, lnext(lc))
+		{
+			p = (Plan*) lfirst(lc);
+			node->plan.total_cost += p->total_cost;
+			node->plan.plan_rows  += p->plan_rows;
+			total_size            += p->plan_rows * p->plan_width;
+		}
+		node->plan.plan_width = rint(total_size / node->plan.plan_rows);
+		*sortClauses = root->parse->sortClause;
+		plan = (Plan*)node;
+		if (!op->all)
+			plan = make_union_unique(op, plan, root, tuple_fraction,
+									 &distinctClause);
+	}
 	else
-		plan = make_union_unique(op, plan, root, tuple_fraction, sortClauses);
+	{
+		/*
+		 * Append the child results together.
+		 */
+		plan = (Plan *) make_append(planlist, tlist);
+
+		/*
+		 * For UNION ALL, we just need the Append plan.  For UNION, need to
+		 * add node(s) to remove duplicates.
+		 */
+		*sortClauses = NIL;
+
+		if (!op->all)
+			plan = make_union_unique(op, plan, root, tuple_fraction, sortClauses);
+	}
 
 	/*
 	 * Estimate number of groups if caller wants it.  For now we just assume
@@ -544,12 +758,14 @@ generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,
 	lplan = recurse_set_operations(op->larg, root,
 								   0.0 /* all tuples needed */ ,
 								   op->colTypes, op->colCollations,
+								   op->groupClauses,
 								   false, 0,
 								   refnames_tlist,
 								   &child_sortclauses, &dLeftGroups);
 	rplan = recurse_set_operations(op->rarg, root,
 								   0.0 /* all tuples needed */ ,
 								   op->colTypes, op->colCollations,
+								   op->groupClauses,
 								   false, 1,
 								   refnames_tlist,
 								   &child_sortclauses, &dRightGroups);
@@ -589,7 +805,7 @@ generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,
 	plan = (Plan *) make_append(planlist, tlist);
 
 	/* Identify the grouping semantics */
-	groupList = generate_setop_grouplist(op, tlist);
+	groupList = generate_setop_grouplist(op->groupClauses, tlist, NULL);
 
 	/* punt if nothing to group on (can this happen?) */
 	if (groupList == NIL)
@@ -678,9 +894,13 @@ static List *
 recurse_union_children(Node *setOp, PlannerInfo *root,
 					   double tuple_fraction,
 					   SetOperationStmt *top_union,
-					   List *refnames_tlist)
+					   List *refnames_tlist,
+					   List **child_sortclauses)
 {
-	List	   *child_sortclauses;
+	List	   *lplan, *rplan;
+	List	   *lsort, *rsort;
+
+	*child_sortclauses = NIL;
 
 	if (IsA(setOp, SetOperationStmt))
 	{
@@ -691,14 +911,23 @@ recurse_union_children(Node *setOp, PlannerInfo *root,
 			equal(op->colTypes, top_union->colTypes))
 		{
 			/* Same UNION, so fold children into parent's subplan list */
-			return list_concat(recurse_union_children(op->larg, root,
-													  tuple_fraction,
-													  top_union,
-													  refnames_tlist),
-							   recurse_union_children(op->rarg, root,
-													  tuple_fraction,
-													  top_union,
-													  refnames_tlist));
+			lplan = recurse_union_children(op->larg, root,
+										   tuple_fraction,
+										   top_union,
+										   refnames_tlist,
+										   &lsort);
+			rplan = recurse_union_children(op->rarg, root,
+										   tuple_fraction,
+										   top_union,
+										   refnames_tlist,
+										   &rsort);
+			/*
+			 * Propagate whether all the descendents are sorted with same
+			 *  sortClause.
+			 */
+			if (lsort != NIL && equal(lsort, rsort))
+				*child_sortclauses = lsort;
+			return list_concat(lplan, rplan);
 		}
 	}
 
@@ -715,13 +944,16 @@ recurse_union_children(Node *setOp, PlannerInfo *root,
 											 tuple_fraction,
 											 top_union->colTypes,
 											 top_union->colCollations,
+											 top_union->groupClauses,
 											 false, -1,
 											 refnames_tlist,
-											 &child_sortclauses, NULL));
+											 child_sortclauses, NULL));
 }
 
 /*
  * Add nodes to the given plan tree to unique-ify the result of a UNION.
+ *
+ * If the sortClause is given, we assume the plan is already sorted on it.
  */
 static Plan *
 make_union_unique(SetOperationStmt *op, Plan *plan,
@@ -731,9 +963,11 @@ make_union_unique(SetOperationStmt *op, Plan *plan,
 	List	   *groupList;
 	double		dNumGroups;
 	long		numGroups;
+	bool		sort_needed = true;
 
 	/* Identify the grouping semantics */
-	groupList = generate_setop_grouplist(op, plan->targetlist);
+	groupList = generate_setop_grouplist(op->groupClauses,
+										 plan->targetlist, *sortClauses);
 
 	/* punt if nothing to group on (can this happen?) */
 	if (groupList == NIL)
@@ -742,6 +976,9 @@ make_union_unique(SetOperationStmt *op, Plan *plan,
 		return plan;
 	}
 
+	if (*sortClauses && equal(*sortClauses, groupList))
+		sort_needed = false;
+	
 	/*
 	 * XXX for the moment, take the number of distinct groups as equal to the
 	 * total input size, ie, the worst case.  This is too conservative, but we
@@ -756,7 +993,8 @@ make_union_unique(SetOperationStmt *op, Plan *plan,
 	numGroups = (long) Min(dNumGroups, (double) LONG_MAX);
 
 	/* Decide whether to hash or sort */
-	if (choose_hashed_setop(root, groupList, plan,
+	if (sort_needed &&
+		choose_hashed_setop(root, groupList, plan,
 							dNumGroups, dNumGroups, tuple_fraction,
 							"UNION"))
 	{
@@ -778,7 +1016,9 @@ make_union_unique(SetOperationStmt *op, Plan *plan,
 	else
 	{
 		/* Sort and Unique */
-		plan = (Plan *) make_sort_from_sortclauses(root, groupList, plan);
+		if (sort_needed)
+			plan = (Plan *) make_sort_from_sortclauses(root, groupList, plan);
+		
 		plan = (Plan *) make_unique(plan, groupList);
 		plan->plan_rows = dNumGroups;
 		/* We know the sort order of the result */
@@ -1145,23 +1385,34 @@ generate_append_tlist(List *colTypes, List *colCollations,
  * the parser output representation doesn't include a tlist for each
  * setop.  So what we need to do here is copy that list and install
  * proper sortgrouprefs into it and into the targetlist.
+ *
+ * sortClause is consulted if provided to avoid re-sorting with different
+ * orderings on the same keys later.
  */
 static List *
-generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
+generate_setop_grouplist(List  *groupClauses, List *targetlist, 
+						 List *sortClauses)
 {
-	List	   *grouplist = (List *) copyObject(op->groupClauses);
+	List	   *grouplist = (List *) copyObject(groupClauses);
 	ListCell   *lg;
+	ListCell   *ls = NULL;
 	ListCell   *lt;
 	Index		refno = 1;
 
 	lg = list_head(grouplist);
+
+	if (sortClauses)
+		ls = list_head(sortClauses);
+
 	foreach(lt, targetlist)
 	{
 		TargetEntry *tle = (TargetEntry *) lfirst(lt);
-		SortGroupClause *sgc;
+		SortGroupClause *sgc, *sgc_sort = NULL;
 
-		/* tlist shouldn't have any sortgrouprefs yet */
-		Assert(tle->ressortgroupref == 0);
+		/*
+		 * tlist shouldn't have any sortgrouprefs yet, or should be unchanged
+		 */
+		Assert(tle->ressortgroupref == 0 || tle->ressortgroupref == refno);
 
 		if (tle->resjunk)
 			continue;			/* ignore resjunk columns */
@@ -1174,6 +1425,45 @@ generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
 
 		/* we could use assignSortGroupRef here, but seems a bit silly */
 		sgc->tleSortGroupRef = tle->ressortgroupref = refno++;
+		
+		if (ls)
+		{
+			/*
+			 * If sortClauses is provided, try to adjust the sorting order to
+			 * get the chance for omitting sorting for grouping later.
+			 */
+			sgc_sort = (SortGroupClause *) lfirst(ls);
+			ls = lnext(ls);
+			if (sgc->sortop != sgc_sort->sortop)
+			{
+				Oid reverse = InvalidOid;
+				Oid opfamily, opcintype;
+				int16 strategy;
+				
+				if (get_ordering_op_properties(sgc->sortop,
+								&opfamily, &opcintype, &strategy))
+				{
+					switch (strategy)
+					{
+						case BTLessStrategyNumber:
+							strategy = BTGreaterStrategyNumber; break;
+						case BTGreaterStrategyNumber:
+							strategy = BTLessStrategyNumber; break;
+					}
+
+					reverse = get_opfamily_member(opfamily,
+												  opcintype,
+												  opcintype,
+												  strategy);
+					if (sgc_sort->sortop == reverse)
+					{
+						sgc->sortop = reverse;
+						sgc->nulls_first = sgc_sort->nulls_first;
+					}
+				}
+			}
+		}
+			
 	}
 	Assert(lg == NULL);
 	return grouplist;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to