Re: Using indices for UNION. - Mailing list pgsql-hackers

From Kyotaro HORIGUCHI
Subject Re: Using indices for UNION.
Date
Msg-id 20131119.204158.141802657.horiguchi.kyotaro@lab.ntt.co.jp
Whole thread Raw
In response to Re: Using indices for UNION.  (Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp>)
Responses Re: Using indices for UNION.  (Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp>)
List pgsql-hackers
Hello, I've totally refactored the series of pathes and cut out
the appropriate portion as 'UNION stuff'.

This patch rquires another 'pathkeys expansion using fully-orderd
index' patch to work.

http://www.postgresql.org/message-id/20131119.203516.251520490.horiguchi.kyotaro@lab.ntt.co.jp

1. plan_with_pathkeys_v1_20131119.patch
This is a patch adding pathkeys (and uniqueness) information onstruct Plan to do what the latter part of
grouping_plannerdoeson current_pathkeys in seemingly autonomous way. As in theprevious discussion, this is in a sense a
baddirection togo. But this patch make the path manipulations occured in thelatter of grouping_planner simple and would
reduceextra sortingand uniq'ing.
 

2. union_uses_idx_v3_20131119.patch
This is made to apply after the first patch above. The core of"Using indices for UNION". This is - as in the
previousdiscussion- also not in so smart way to do that. Most of all,this patch runs subquery_planner additionally for
UNIONwithsome conditions. Nevertheless, the expected gain should not besmall.
 


regards,

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 5947e5b..d8a17a0 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -98,11 +98,13 @@ static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);static IndexScan
*make_indexscan(List*qptlist, List *qpqual, Index scanrelid,               Oid indexid, List *indexqual, List
*indexqualorig,              List *indexorderby, List *indexorderbyorig,
 
+               List *pathkeys, bool uniquely_ordered,               ScanDirection indexscandir);static IndexOnlyScan
*make_indexonlyscan(List*qptlist, List *qpqual,                   Index scanrelid, Oid indexid,                   List
*indexqual,List *indexorderby,                   List *indextlist,
 
+                   List *pathkeys, bool uniquely_ordered,                   ScanDirection indexscandir);static
BitmapIndexScan*make_bitmap_indexscan(Index scanrelid, Oid indexid,                      List *indexqual,
 
@@ -150,8 +152,8 @@ static MergeJoin *make_mergejoin(List *tlist,               bool *mergenullsfirst,
Plan*lefttree, Plan *righttree,               JoinType jointype);
 
-static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
-          AttrNumber *sortColIdx, Oid *sortOperators,
+static Sort *make_sort(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+          int numCols,  AttrNumber *sortColIdx, Oid *sortOperators,          Oid *collations, bool *nullsFirst,
 double limit_tuples);static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
 
@@ -750,6 +752,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)    plan->qual = NIL;
plan->lefttree= NULL;    plan->righttree = NULL;
 
+    plan->pathkeys = pathkeys;
+    plan->is_unique = false;    /* Compute sort column info, and adjust MergeAppend's tlist as needed */    (void)
prepare_sort_from_pathkeys(root,plan, pathkeys,
 
@@ -810,7 +814,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)        /* Now, insert a
Sortnode if subplan isn't sufficiently ordered */        if (!pathkeys_contained_in(pathkeys, subpath->pathkeys))
 
-            subplan = (Plan *) make_sort(root, subplan, numsortkeys,
+            subplan = (Plan *) make_sort(root, subplan, pathkeys, numsortkeys,
sortColIdx,sortOperators,                                         collations, nullsFirst,
         best_path->limit_tuples);
 
@@ -1263,6 +1267,8 @@ create_indexscan_plan(PlannerInfo *root,
fixed_indexquals,                                               fixed_indexorderbys,
       best_path->indexinfo->indextlist,
 
+                                                best_path->path.pathkeys,
+                                                false,
best_path->indexscandir);   else        scan_plan = (Scan *) make_indexscan(tlist,
 
@@ -1273,6 +1279,8 @@ create_indexscan_plan(PlannerInfo *root,
stripped_indexquals,                                           fixed_indexorderbys,
      indexorderbys,
 
+                                            best_path->path.pathkeys,
+                                            false,
best_path->indexscandir);   copy_path_costsize(&scan_plan->plan, &best_path->path);
 
@@ -3245,6 +3253,8 @@ make_indexscan(List *qptlist,               List *indexqualorig,               List
*indexorderby,              List *indexorderbyorig,
 
+               List *pathkeys,
+               bool  uniquely_ordered,               ScanDirection indexscandir){    IndexScan  *node =
makeNode(IndexScan);
@@ -3255,6 +3265,8 @@ make_indexscan(List *qptlist,    plan->qual = qpqual;    plan->lefttree = NULL;
plan->righttree= NULL;
 
+    plan->pathkeys = pathkeys;
+    plan->is_unique = uniquely_ordered;    node->scan.scanrelid = scanrelid;    node->indexid = indexid;
node->indexqual= indexqual;
 
@@ -3274,6 +3286,8 @@ make_indexonlyscan(List *qptlist,                   List *indexqual,                   List
*indexorderby,                  List *indextlist,
 
+                   List *pathkeys,
+                   bool  uniquely_ordered,                   ScanDirection indexscandir){    IndexOnlyScan *node =
makeNode(IndexOnlyScan);
@@ -3284,6 +3298,8 @@ make_indexonlyscan(List *qptlist,    plan->qual = qpqual;    plan->lefttree = NULL;
plan->righttree= NULL;
 
+    plan->pathkeys = pathkeys;
+    plan->is_unique = uniquely_ordered;    node->scan.scanrelid = scanrelid;    node->indexid = indexid;
node->indexqual= indexqual;
 
@@ -3753,8 +3769,8 @@ make_mergejoin(List *tlist, * limit_tuples is as for cost_sort (in particular, pass -1 if no
limit)*/static Sort *
 
-make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
-          AttrNumber *sortColIdx, Oid *sortOperators,
+make_sort(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+          int numCols,  AttrNumber *sortColIdx, Oid *sortOperators,          Oid *collations, bool *nullsFirst,
 double limit_tuples){
 
@@ -3776,6 +3792,8 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols,    plan->qual = NIL;    plan->lefttree
=lefttree;    plan->righttree = NULL;
 
+    plan->pathkeys = pathkeys;
+    plan->is_unique = false;    node->numCols = numCols;    node->sortColIdx = sortColIdx;    node->sortOperators =
sortOperators;
@@ -4125,7 +4143,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
                 &nullsFirst);    /* Now build the Sort node */
 
-    return make_sort(root, lefttree, numsortkeys,
+    return make_sort(root, lefttree, pathkeys, numsortkeys,                     sortColIdx, sortOperators, collations,
                   nullsFirst, limit_tuples);}
 
@@ -4147,6 +4165,7 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)    Oid
*sortOperators;   Oid           *collations;    bool       *nullsFirst;
 
+    List        *pathkeys;    /* Convert list-ish representation to arrays wanted by executor */    numsortkeys =
list_length(sortcls);
@@ -4168,7 +4187,9 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
numsortkeys++;   }
 
-    return make_sort(root, lefttree, numsortkeys,
+    pathkeys = make_pathkeys_for_sortclauses(root, sortcls, sub_tlist);
+
+    return make_sort(root, lefttree, pathkeys, numsortkeys,                     sortColIdx, sortOperators, collations,
                   nullsFirst, -1.0);}
 
@@ -4199,6 +4220,7 @@ make_sort_from_groupcols(PlannerInfo *root,    Oid           *sortOperators;    Oid
*collations;   bool       *nullsFirst;
 
+    List       *pathkeys;    /* Convert list-ish representation to arrays wanted by executor */    numsortkeys =
list_length(groupcls);
@@ -4220,10 +4242,17 @@ make_sort_from_groupcols(PlannerInfo *root,        sortOperators[numsortkeys] = grpcl->sortop;
     collations[numsortkeys] = exprCollation((Node *) tle->expr);        nullsFirst[numsortkeys] = grpcl->nulls_first;
 
+
+        /* This tlist should not be bound with any other orderig clause */
+        Assert(tle->ressortgroupref == 0);
+        tle->ressortgroupref = grpcl->tleSortGroupRef;
+        numsortkeys++;    }
-    return make_sort(root, lefttree, numsortkeys,
+    pathkeys = make_pathkeys_for_sortclauses(root, groupcls, sub_tlist);
+
+    return make_sort(root, lefttree, pathkeys, numsortkeys,                     sortColIdx, sortOperators, collations,
                   nullsFirst, -1.0);}
 
@@ -4339,6 +4368,16 @@ make_agg(PlannerInfo *root, List *tlist, List *qual,    plan->lefttree = lefttree;
plan->righttree= NULL;
 
+    /*
+     * We can safely assume that the lefttree and therefore the result is
+     * sorted on group pathkeys and unique if given aggstrategy is AGG_SORTED.
+     */
+    if (node->aggstrategy == AGG_SORTED)
+        plan->pathkeys =  root->group_pathkeys;
+    else
+        plan->pathkeys =  NIL;
+    plan->is_unique = true;
+    return node;}
@@ -4448,6 +4487,13 @@ make_group(PlannerInfo *root,    plan->lefttree = lefttree;    plan->righttree = NULL;
+    /*
+     * We assume that lefttree is ordered on the same pathkeys with that this
+     * group node wants.
+     */
+    plan->pathkeys = lefttree->pathkeys;
+    plan->is_unique = true;
+    return node;}
@@ -4486,6 +4532,8 @@ make_unique(Plan *lefttree, List *distinctList)    plan->qual = NIL;    plan->lefttree =
lefttree;   plan->righttree = NULL;
 
+    plan->pathkeys = lefttree->pathkeys;
+    plan->is_unique = true;    /*     * convert SortGroupClause list into arrays of attr indexes and equality
@@ -4669,6 +4717,8 @@ make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,    plan->qual = NIL;
plan->lefttree= lefttree;    plan->righttree = NULL;
 
+    plan->pathkeys = lefttree->pathkeys;
+    plan->is_unique = lefttree->is_unique;    node->limitOffset = limitOffset;    node->limitCount = limitCount;
@@ -4717,6 +4767,10 @@ make_result(PlannerInfo *root,    plan->qual = NIL;    plan->lefttree = subplan;
plan->righttree= NULL;
 
+
+    /* this plan emits at most one row when subplan is NULL */
+    if (subplan == NULL) plan->is_unique = true;
+    node->resconstantqual = resconstantqual;    return node;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index d8aa35d..a09d38f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1011,6 +1011,19 @@ inheritance_planner(PlannerInfo *root)
SS_assign_special_param(root));}
+static bool
+plan_is_ordered(Plan *plan, List *pathkeys)
+{
+    if (pathkeys_contained_in(pathkeys, plan->pathkeys))
+        return true;
+
+    if (plan->pathkeys && plan->is_unique &&
+        pathkeys_contained_in(plan->pathkeys, pathkeys))
+        return true;
+
+    return false;
+}
+/*-------------------- * grouping_planner *      Perform planning steps related to grouping, aggregation, etc.
@@ -1039,7 +1052,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)    int64        count_est = 0;
double       limit_tuples = -1.0;    Plan       *result_plan;
 
-    List       *current_pathkeys;    double        dNumGroups = 0;    bool        use_hashed_distinct = false;    bool
      tested_hashed_distinct = false;
 
@@ -1081,15 +1093,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
  &set_sortclauses);        /*
 
-         * Calculate pathkeys representing the sort order (if any) of the set
-         * operation's result.  We have to do this before overwriting the sort
-         * key information...
-         */
-        current_pathkeys = make_pathkeys_for_sortclauses(root,
-                                                         set_sortclauses,
-                                                    result_plan->targetlist);
-
-        /*         * We should not need to call preprocess_targetlist, since we must be         * in a SELECT query
node.   Instead, use the targetlist returned by         * plan_set_operations (since this tells whether it returned
any
@@ -1442,15 +1445,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
         tlist,                                                 &agg_costs,
   best_path);
 
-        if (result_plan != NULL)
-        {
-            /*
-             * optimize_minmax_aggregates generated the full plan, with the
-             * right tlist, and it has no sort order.
-             */
-            current_pathkeys = NIL;
-        }
-        else
+        if (result_plan == NULL)        {            /*             * Normal case --- create a plan according to
query_planner's
@@ -1459,11 +1454,10 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)            bool
need_sort_for_grouping= false;            result_plan = create_plan(root, best_path);
 
-            current_pathkeys = best_path->pathkeys;            /* Detect if we'll need an explicit sort for grouping
*/           if (parse->groupClause && !use_hashed_grouping &&
 
-              !pathkeys_contained_in(root->group_pathkeys, current_pathkeys))
+                !plan_is_ordered(result_plan, root->group_pathkeys))            {
need_sort_for_grouping= true;
 
@@ -1541,37 +1535,20 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
extract_grouping_ops(parse->groupClause),                                               numGroups,
                         result_plan);
 
-                /* Hashed aggregation produces randomly-ordered results */
-                current_pathkeys = NIL;            }            else if (parse->hasAggs)            {
/*Plain aggregate plan --- sort if needed */                AggStrategy aggstrategy;
 
-                if (parse->groupClause)
+                aggstrategy = parse->groupClause ? AGG_SORTED : AGG_PLAIN;
+                if (aggstrategy == AGG_SORTED && need_sort_for_grouping)                {
-                    if (need_sort_for_grouping)
-                    {
-                        result_plan = (Plan *)
-                            make_sort_from_groupcols(root,
-                                                     parse->groupClause,
-                                                     groupColIdx,
-                                                     result_plan);
-                        current_pathkeys = root->group_pathkeys;
-                    }
-                    aggstrategy = AGG_SORTED;
-
-                    /*
-                     * The AGG node will not change the sort ordering of its
-                     * groups, so current_pathkeys describes the result too.
-                     */
-                }
-                else
-                {
-                    aggstrategy = AGG_PLAIN;
-                    /* Result will be only one row anyway; no sort order */
-                    current_pathkeys = NIL;
+                    result_plan = (Plan *)
+                        make_sort_from_groupcols(root,
+                                                 parse->groupClause,
+                                                 groupColIdx,
+                                                 result_plan);                }                result_plan = (Plan *)
make_agg(root,
@@ -1601,7 +1578,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
        parse->groupClause,                                                 groupColIdx,
                result_plan);
 
-                    current_pathkeys = root->group_pathkeys;                }                result_plan = (Plan *)
make_group(root,
@@ -1612,7 +1588,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
extract_grouping_ops(parse->groupClause),                                                 dNumGroups,
                              result_plan);
 
-                /* The Group node won't change sort ordering */            }            else if (root->hasHavingQual)
         {
 
@@ -1722,13 +1697,14 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                 result_plan,                                                        window_pathkeys,
                                    -1.0);
 
-                    if (!pathkeys_contained_in(window_pathkeys,
-                                               current_pathkeys))
-                    {
-                        /* we do indeed need to sort */
+
+                    /*
+                     * we do indeed need to sort if result_plan is not ordered
+                     * on window_pathkeys
+                     */
+                    if (!plan_is_ordered(result_plan, window_pathkeys))                        result_plan = (Plan *)
sort_plan;
-                        current_pathkeys = window_pathkeys;
-                    }
+                    /* In either case, extract the per-column information */
get_column_info_for_window(root,wc, tlist,                                               sort_plan->numCols,
 
@@ -1792,12 +1768,12 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)        long        numDistinctRows;
     /*
 
-         * If there was grouping or aggregation, use the current number of
-         * rows as the estimated number of DISTINCT rows (ie, assume the
-         * result was already mostly unique).  If not, use the number of
+         * If result_plan emits already distinct'ed rows, use the current
+         * number of rows as the estimated number of DISTINCT rows (ie, assume
+         * the result was already unique).  If not, use the number of         * distinct-groups calculated previously.
       */
 
-        if (parse->groupClause || root->hasHavingQual || parse->hasAggs)
+        if (result_plan->is_unique)            dNumDistinctRows = result_plan->plan_rows;        else
dNumDistinctRows= dNumGroups;
 
@@ -1822,7 +1798,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
result_plan->total_cost,                                      result_plan->startup_cost,
      result_plan->total_cost,
 
-                                       current_pathkeys,
+                                       result_plan->pathkeys,                                       dNumDistinctRows);
      }
 
@@ -1840,8 +1816,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
extract_grouping_ops(parse->distinctClause),                                           numDistinctRows,
                          result_plan);
 
-            /* Hashed aggregation produces randomly-ordered results */
-            current_pathkeys = NIL;        }        else        {
@@ -1865,29 +1839,23 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)            else
needed_pathkeys= root->distinct_pathkeys;
 
-            if (!pathkeys_contained_in(needed_pathkeys, current_pathkeys))
+            if (!plan_is_ordered(result_plan, needed_pathkeys))            {
-                if (list_length(root->distinct_pathkeys) >=
-                    list_length(root->sort_pathkeys))
-                    current_pathkeys = root->distinct_pathkeys;
-                else
-                {
-                    current_pathkeys = root->sort_pathkeys;
-                    /* Assert checks that parser didn't mess up... */
-                    Assert(pathkeys_contained_in(root->distinct_pathkeys,
-                                                 current_pathkeys));
-                }
-
+                /* Assert checks that parser didn't mess up... */
+                Assert(pathkeys_contained_in(root->distinct_pathkeys,
+                                             needed_pathkeys));
+                Assert(pathkeys_contained_in(root->sort_pathkeys,
+                                             needed_pathkeys));                result_plan = (Plan *)
make_sort_from_pathkeys(root,                                                              result_plan,
 
-                                                            current_pathkeys,
+                                                            needed_pathkeys,
                   -1.0);            }
 
-            result_plan = (Plan *) make_unique(result_plan,
-                                               parse->distinctClause);
+            if (!result_plan->is_unique)
+                result_plan = (Plan *) make_unique(result_plan,
+                                                   parse->distinctClause);            result_plan->plan_rows =
dNumDistinctRows;
-            /* The Unique node won't change sort ordering */        }    }
@@ -1897,13 +1865,12 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)     */    if (parse->sortClause)
{
-        if (!pathkeys_contained_in(root->sort_pathkeys, current_pathkeys))
+        if (!plan_is_ordered(result_plan, root->sort_pathkeys))        {            result_plan = (Plan *)
make_sort_from_pathkeys(root,                                                          result_plan,
                                   root->sort_pathkeys,
limit_tuples);
-            current_pathkeys = root->sort_pathkeys;        }    }
@@ -1914,18 +1881,10 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)     * ModifyTable node instead.)
*/   if (parse->rowMarks)
 
-    {        result_plan = (Plan *) make_lockrows(result_plan,
root->rowMarks,                                            SS_assign_special_param(root));
 
-        /*
-         * The result can no longer be assumed sorted, since locking might
-         * cause the sort key columns to be replaced with new values.
-         */
-        current_pathkeys = NIL;
-    }
-    /*     * Finally, if there is a LIMIT/OFFSET clause, add the LIMIT node.     */
@@ -1942,7 +1901,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)     * Return the actual output
orderingin query_pathkeys for possible use by     * an outer query level.     */
 
-    root->query_pathkeys = current_pathkeys;
+    root->query_pathkeys = result_plan->pathkeys;    return result_plan;}
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 44ea0b7..ced07d9 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -101,6 +101,8 @@ typedef struct Plan     */    double        plan_rows;        /* number of rows plan is expected to
emit*/    int            plan_width;        /* average row width in bytes */
 
+    List       *pathkeys;        /* ordered on this pathkeys if any */
+    bool        is_unique;        /* emits unique result */    /*     * Common structural data for all Plan types.
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index d8a17a0..8c60b2c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -813,7 +813,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
numsortkeys* sizeof(bool)) == 0);        /* Now, insert a Sort node if subplan isn't sufficiently ordered */
 
-        if (!pathkeys_contained_in(pathkeys, subpath->pathkeys))
+        if (!path_is_ordered(subpath, pathkeys))            subplan = (Plan *) make_sort(root, subplan, pathkeys,
numsortkeys,                                        sortColIdx, sortOperators,
collations,nullsFirst,
 
@@ -1151,6 +1151,7 @@ create_indexscan_plan(PlannerInfo *root,    List       *fixed_indexquals;    List
*fixed_indexorderbys;   ListCell   *l;
 
+    bool        uniquely_ordered = false;    /* it should be a base rel... */    Assert(baserelid > 0);
@@ -1258,6 +1259,20 @@ create_indexscan_plan(PlannerInfo *root,            replace_nestloop_params(root, (Node *)
indexorderbys);   }
 
+    /*
+     * XXX: This is rather tricky. IndexPath's pathkeys may be both superset
+     * (including the same) or subset of key columns of the index. This path
+     * will emit distnct'edly ordered rows when the pathkeys contains the key
+     * columns and the index is fully ordered on the key columns.
+     *
+     * See the point calling truncate_useless_pathkeys in build_index_paths()
+     * for detail.
+     */
+    if (list_length(best_path->path.pathkeys) >=
+        best_path->indexinfo->ncolumns &&
+        best_path->indexinfo->full_ordered)
+        uniquely_ordered = true;
+    /* Finally ready to build the plan node */    if (indexonly)        scan_plan = (Scan *)
make_indexonlyscan(tlist,
@@ -1268,7 +1283,7 @@ create_indexscan_plan(PlannerInfo *root,
fixed_indexorderbys,                                           best_path->indexinfo->indextlist,
                       best_path->path.pathkeys,
 
-                                                false,
+                                                uniquely_ordered,
best_path->indexscandir);   else        scan_plan = (Scan *) make_indexscan(tlist,
 
@@ -1280,7 +1295,7 @@ create_indexscan_plan(PlannerInfo *root,
fixed_indexorderbys,                                           indexorderbys,
best_path->path.pathkeys,
 
-                                            false,
+                                            uniquely_ordered,
best_path->indexscandir);   copy_path_costsize(&scan_plan->plan, &best_path->path);
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a09d38f..a337c84 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1075,15 +1075,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
perhapsa top-level sort and/or LIMIT.  Note that         * any special work for recursive unions is the responsibility
of
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index e249628..9fe0b66 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"
@@ -44,6 +45,7 @@#include "optimizer/planner.h"#include "optimizer/prep.h"#include "optimizer/tlist.h"
+#include "optimizer/paths.h"#include "parser/parse_coerce.h"#include "parser/parsetree.h"#include "utils/lsyscache.h"
@@ -60,7 +62,7 @@ typedef structstatic Plan *recurse_set_operations(Node *setOp, PlannerInfo *root,
 double tuple_fraction,                       List *colTypes, List *colCollations,
 
-                       bool junkOK,
+                       List *groupClauses, bool junkOK,                       int flag, List *refnames_tlist,
            List **sortClauses, double *pNumGroups);static Plan *generate_recursion_plan(SetOperationStmt *setOp,
 
@@ -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
entriesin 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:
fractionof 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,7 +259,7 @@ static Plan *recurse_set_operations(Node *setOp, PlannerInfo *root,                       double
tuple_fraction,                      List *colTypes, List *colCollations,
 
-                       bool junkOK,
+                       List *groupClauses, bool junkOK,                       int flag, List *refnames_tlist,
            List **sortClauses, double *pNumGroups){
 
@@ -224,13 +268,20 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,        RangeTblRef *rtr = (RangeTblRef *)
setOp;       RangeTblEntry *rte = root->simple_rte_array[rtr->rtindex];        Query       *subquery = rte->subquery;
 
+        Query       *pdquery = NULL; /* 'pd' comes from 'pushed down' */        RelOptInfo *rel;
-        PlannerInfo *subroot;
+        PlannerInfo *subroot,
+                    *pdsubroot;
+        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 +294,146 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,        /*         * Generate plan for
primitivesubquery
 
+         *
+         * Consider pusing down ORDER BY or groupings of the parent set
+         * operation 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 match in types and ressortgroupref should be 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 != 0 &&
+                     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)
+            {
+                if (pdquery->sortClause)
+                {
+                    ListCell *lct;
+                    int i = 1;
+
+                    /* Check if groupClause is coappliable with sortClauses */
+                    foreach (lct, pdquery->targetList)
+                    {
+                        TargetEntry *te = (TargetEntry *) lfirst(lct);
+
+                        if (te->resjunk) continue;
+                        if (te->ressortgroupref != 0 &&
+                            te->ressortgroupref != i)
+                            break;
+                        i++;
+                    }
+
+                    if (!lct)
+                        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);
+
+            /*
+             * Save current PlnannerGlobal. subquery_planner could modify
+             * PlannerGlobal so make a shallow backup in order to make the
+             * alternative plans selectable.
+             */
+            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)
+            {
+                subquery = pdquery;
+                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;
 
@@ -291,10 +476,28 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,                              subplan);
  /*
 
-         * We don't bother to determine the subquery's output ordering since
-         * it won't be reflected in the set-op result anyhow.
+         * This plan's pathkeys and is_unique are apparently same to them of
+         * subplan unless type casting or coercing collations.         */
-        *sortClauses = NIL;
+        if (subplan->pathkeys &&
+            tlist_same_datatypes(plan->targetlist, colTypes, junkOK) &&
+            tlist_same_collations(plan->targetlist, colCollations, junkOK))
+        {
+            ListCell *lcp, *lcr;
+
+            forboth (lcp, plan->targetlist, lcr, root->parse->targetList)
+            {
+                TargetEntry *tep = (TargetEntry*) lfirst(lcp);
+                TargetEntry *ter = (TargetEntry*) lfirst(lcr);
+
+                tep->ressortgroupref = ter->ressortgroupref;
+            }
+            plan->pathkeys =
+                make_pathkeys_for_sortclauses(root,
+                                              *sortClauses,
+                                              plan->targetlist);
+            plan->is_unique = subplan->is_unique;
+        }        return plan;    }
@@ -379,12 +582,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 +614,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 +658,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 +668,106 @@ 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);
+    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;
+            te->ressortgroupref = sgc->tleSortGroupRef;
+            i++;
+        }
+        node->plan.pathkeys =
+            make_pathkeys_for_sortclauses(root, lsort, tlist);
+
+        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;
-    /*
-     * 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 */
+        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 +809,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,8 +856,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 +944,11 @@ 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;    if (IsA(setOp, SetOperationStmt))    {
@@ -690,15 +958,23 @@ recurse_union_children(Node *setOp, PlannerInfo *root,            (op->all == top_union->all ||
op->all)&&            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 +991,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-ifythe result of a UNION.
 
+ *
+ * Given a sortClause, we assume the plan is already sorted on it. */static Plan *make_union_unique(SetOperationStmt
*op,Plan *plan,
 
@@ -731,9 +1010,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 +1023,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
worstcase.  This is too conservative, but we
 
@@ -756,7 +1040,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 +1063,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
orderof the result */
 
@@ -1145,23 +1432,34 @@ generate_append_tlist(List *colTypes, List *colCollations, * the parser output representation
doesn'tinclude a tlist for each * setop.  So what we need to do here is copy that list and install * proper
sortgrouprefsinto it and into the targetlist.
 
+ *
+ * sortClause is consulted if provided to avoid re-sorting in different
+ * directions 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 +1472,45 @@ generate_setop_grouplist(SetOperationStmt *op, List *targetlist)        /* we could use
assignSortGroupRefhere, 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;

pgsql-hackers by date:

Previous
From: Kyotaro HORIGUCHI
Date:
Subject: Re: Get more from indices.
Next
From: Craig Ringer
Date:
Subject: Re: Windows build patch