Cause GROUP BY clause to adopt ordering operators from ORDER BY when
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 15 Jun 2003 16:42:08 +0000 (16:42 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 15 Jun 2003 16:42:08 +0000 (16:42 +0000)
both clauses specify the same targets, rather than always using the
default ordering operator.  This allows 'GROUP BY foo ORDER BY foo DESC'
to be done with only one sort step.

src/backend/parser/analyze.c
src/backend/parser/parse_clause.c
src/include/parser/parse_clause.h

index 9ac8132f08a22883d7d8a7e46099c47d829f2193..95b209e4acb2fca56b81c005fc7572ec90ae727a 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.273 2003/06/06 15:04:02 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.274 2003/06/15 16:42:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1787,14 +1787,19 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
     */
    qry->havingQual = transformWhereClause(pstate, stmt->havingClause);
 
-   qry->groupClause = transformGroupClause(pstate,
-                                           stmt->groupClause,
-                                           qry->targetList);
-
+   /*
+    * Transform sorting/grouping stuff.  Do ORDER BY first because both
+    * transformGroupClause and transformDistinctClause need the results.
+    */
    qry->sortClause = transformSortClause(pstate,
                                          stmt->sortClause,
                                          qry->targetList);
 
+   qry->groupClause = transformGroupClause(pstate,
+                                           stmt->groupClause,
+                                           qry->targetList,
+                                           qry->sortClause);
+
    qry->distinctClause = transformDistinctClause(pstate,
                                                  stmt->distinctClause,
                                                  qry->targetList,
index a29eb007fb769566ef5a093dabbfd02e0ec74ee7..2e2beabddcbe11b0c2b767a6fc48631e65369481 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.114 2003/06/06 15:04:02 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.115 2003/06/15 16:42:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1124,7 +1124,8 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
  *   transform a GROUP BY clause
  */
 List *
-transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
+transformGroupClause(ParseState *pstate, List *grouplist,
+                    List *targetlist, List *sortClause)
 {
    List       *glist = NIL,
               *gl;
@@ -1132,21 +1133,41 @@ transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
    foreach(gl, grouplist)
    {
        TargetEntry *tle;
+       Oid         ordering_op;
+       GroupClause *grpcl;
 
        tle = findTargetlistEntry(pstate, lfirst(gl),
                                  targetlist, GROUP_CLAUSE);
 
        /* avoid making duplicate grouplist entries */
-       if (!targetIsInSortList(tle, glist))
-       {
-           GroupClause *grpcl = makeNode(GroupClause);
-
-           grpcl->tleSortGroupRef = assignSortGroupRef(tle, targetlist);
-
-           grpcl->sortop = ordering_oper_opid(tle->resdom->restype);
+       if (targetIsInSortList(tle, glist))
+           continue;
 
-           glist = lappend(glist, grpcl);
+       /*
+        * If the GROUP BY clause matches the ORDER BY clause, we want to
+        * adopt the ordering operators from the latter rather than using
+        * the default ops.  This allows "GROUP BY foo ORDER BY foo DESC" to
+        * be done with only one sort step.  Note we are assuming that any
+        * user-supplied ordering operator will bring equal values together,
+        * which is all that GROUP BY needs.
+        */
+       if (sortClause &&
+           ((SortClause *) lfirst(sortClause))->tleSortGroupRef ==
+           tle->resdom->ressortgroupref)
+       {
+           ordering_op = ((SortClause *) lfirst(sortClause))->sortop;
+           sortClause = lnext(sortClause);
        }
+       else
+       {
+           ordering_op = ordering_oper_opid(tle->resdom->restype);
+           sortClause = NIL;   /* disregard ORDER BY once match fails */
+       }
+
+       grpcl = makeNode(GroupClause);
+       grpcl->tleSortGroupRef = assignSortGroupRef(tle, targetlist);
+       grpcl->sortop = ordering_op;
+       glist = lappend(glist, grpcl);
    }
 
    return glist;
index da7e7abec4dd940289eb60b5a70deb06f286341a..12ab7317d9ec2bfc1073d78c554458b4deab22d3 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_clause.h,v 1.30 2003/03/22 01:49:38 tgl Exp $
+ * $Id: parse_clause.h,v 1.31 2003/06/15 16:42:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,7 +22,7 @@ extern int setTargetTable(ParseState *pstate, RangeVar *relation,
 extern bool interpretInhOption(InhOption inhOpt);
 extern Node *transformWhereClause(ParseState *pstate, Node *where);
 extern List *transformGroupClause(ParseState *pstate, List *grouplist,
-                    List *targetlist);
+                    List *targetlist, List *sortClause);
 extern List *transformSortClause(ParseState *pstate, List *orderlist,
                    List *targetlist);
 extern List *transformDistinctClause(ParseState *pstate, List *distinctlist,