Fix GroupBy: enable functions over aggregates and GroupBy-ed fields
authorVadim B. Mikheev
Tue, 29 Apr 1997 04:32:50 +0000 (04:32 +0000)
committerVadim B. Mikheev
Tue, 29 Apr 1997 04:32:50 +0000 (04:32 +0000)
in target list.

src/backend/optimizer/plan/planmain.c
src/backend/parser/analyze.c
src/include/nodes/parsenodes.h

index f94552d3720aba7a14c1e92fb8ed801dde94f087..7f70d76ac6ca0f4098eb4bd5be3112395464995d 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.3 1997/04/05 06:37:37 vadim Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.4 1997/04/29 04:32:50 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -359,8 +359,9 @@ make_groupPlan(List **tlist,
     List *sort_tlist;
     List *sl, *gl;
     List *glc = listCopy (groupClause);
-    List *aggvals = NIL;       /* list of vars of aggregates */
-    int aggvcnt;
+    List *otles = NIL;     /* list of removed non-GroupBy entries */
+    List *otlvars = NIL;   /* list of var in them */
+    int otlvcnt;
     Sort *sortplan;
     Group *grpplan;
     int numCols;
@@ -375,7 +376,8 @@ make_groupPlan(List **tlist,
 
     /*
      * Make template TL for subplan, Sort & Group:
-     * 1. Take away Aggregates and re-set resno-s accordantly.
+     * 1. If there are aggregates (tuplePerGroup is true) then take 
+     *    away non-GroupBy entries and re-set resno-s accordantly.
      * 2. Make grpColIdx
      *
      * Note: we assume that TLEs in *tlist are ordered in accordance
@@ -390,7 +392,7 @@ make_groupPlan(List **tlist,
        {
        GroupClause *grpcl = (GroupClause*)lfirst(gl);
        
-       if ( grpcl->resdom->resno == te->resdom->resno )
+       if ( grpcl->entry->resdom->resno == te->resdom->resno )
        {
            
        resdom = te->resdom;
@@ -403,15 +405,20 @@ make_groupPlan(List **tlist,
        break;
        }
    }
-   if ( resdom == NULL )       /* Not GroupBy-ed entry: remove */
-   {               /* aggregate(s) from Group/Sort TL */
-       if ( IsA (te->expr, Aggreg) )
-       {               /* save Aggregate' Vars */
-           aggvals = nconc (aggvals, pull_var_clause (te->expr));
-           sort_tlist = lremove (lfirst (sl), sort_tlist);
+   /* 
+    * Non-GroupBy entry: remove it from Group/Sort TL if there are 
+    * aggregates in query - it will be evaluated by Aggregate plan
+    */
+   if ( resdom == NULL )
+   {
+       if ( tuplePerGroup )
+       {
+           otlvars = nconc (otlvars, pull_var_clause (te->expr));
+           otles = lcons (te, otles);
+           sort_tlist = lremove (te, sort_tlist);
        }
        else
-           resdom->resno = last_resno++;       /* re-set */
+           te->resdom->resno = last_resno++;
    }
     }
 
@@ -421,12 +428,12 @@ make_groupPlan(List **tlist,
     }
     
     /*
-     * Aggregates were removed from TL - we are to add Vars for them
-     * to the end of TL if there are no such Vars in TL already.
+     * If non-GroupBy entries were removed from TL - we are to add Vars for 
+     * them to the end of TL if there are no such Vars in TL already.
      */
 
-    aggvcnt = length (aggvals);
-    foreach (gl, aggvals)
+    otlvcnt = length (otlvars);
+    foreach (gl, otlvars)
     {
        Var *v = (Var*)lfirst (gl);
        
@@ -437,9 +444,9 @@ make_groupPlan(List **tlist,
        last_resno++;
    }
    else        /* already in TL */
-       aggvcnt--;
+       otlvcnt--;
     }
-    /* Now aggvcnt is number of Vars added in TL for Aggregates */
+    /* Now otlvcnt is number of Vars added in TL for non-GroupBy entries */
     
     /* Make TL for subplan: substitute Vars from subplan TL into new TL */
     sl = flatten_tlist_vars (sort_tlist, subplan->targetlist);
@@ -489,17 +496,28 @@ make_groupPlan(List **tlist,
                            grpColIdx, sortplan);
 
     /* 
-     * Make TL for parent: "restore" Aggregates and
-     * resno-s of others accordantly.
+     * Make TL for parent: "restore" non-GroupBy entries (if they
+     * were removed) and set resno-s of others accordantly.
      */
     sl = sort_tlist;
     sort_tlist = NIL;          /* to be new parent TL */
     foreach (gl, *tlist)
     {
+       List *temp = NIL;
        TargetEntry *te = (TargetEntry *) lfirst (gl);
-
-   if ( !IsA (te->expr, Aggreg) )  /* It's "our" TLE - we're to return */
-   {               /* it from Sort/Group plans */
+       
+       foreach (temp, otles)   /* Is it removed non-GroupBy entry ? */
+       {
+           TargetEntry *ote = (TargetEntry *) lfirst (temp);
+           
+           if ( ote->resdom->resno == te->resdom->resno )
+           {
+           otles = lremove (ote, otles);
+               break;
+           }
+       }
+   if ( temp == NIL )  /* It's "our" TLE - we're to return */
+   {           /* it from Sort/Group plans */
            TargetEntry *my = (TargetEntry *) lfirst (sl);  /* get it */
            
        sl = sl->next;      /* prepare for the next "our" */
@@ -508,15 +526,16 @@ make_groupPlan(List **tlist,
        sort_tlist = lappend (sort_tlist, my);
        continue;
    }
-   /* TLE of an aggregate */
+   /* else - it's TLE of an non-GroupBy entry */
    sort_tlist = lappend (sort_tlist, copyObject(te));
     }
     /* 
-     * Pure aggregates Vars were at the end of Group' TL.
+     * Pure non-GroupBy entries Vars were at the end of Group' TL.
      * They shouldn't appear in parent TL, all others shouldn't
      * disappear.
      */
-    Assert ( aggvcnt == length (sl) );
+    Assert ( otlvcnt == length (sl) );
+    Assert ( length (otles) == 0 );
 
     *tlist = sort_tlist;
     
index 8fc78d3aca0c3fc33936a84836adc4d00115d374..1bc64047d610cc35d0e2ada93bd9fb49adccd5a4 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.26 1997/04/27 19:16:44 thomas Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.27 1997/04/29 04:32:26 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1552,7 +1552,8 @@ transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
    if (restarget == NULL)
        elog(WARN,"The field being grouped by must appear in the target list");
 
-   grpcl->resdom = resdom = restarget->resdom;
+   grpcl->entry = restarget;
+   resdom = restarget->resdom;
    grpcl->grpOpoid = oprid(oper("<",
                   resdom->restype,
                   resdom->restype,false));
@@ -2359,6 +2360,39 @@ contain_agg_clause(Node *clause)
     return FALSE;
 }
 
+/*
+ * exprIsAggOrGroupCol -
+ *    returns true if the expression does not contain non-group columns.
+ */
+static bool
+exprIsAggOrGroupCol(Node *expr, List *groupClause)
+{
+    List *gl;
+    
+    if ( expr == NULL || IsA (expr, Const) || IsA (expr, Aggreg) )
+   return TRUE;
+
+    foreach (gl, groupClause)
+    {
+   GroupClause *grpcl = lfirst(gl);
+   
+   if ( equal (expr, grpcl->entry->expr) )
+       return TRUE;
+    }
+
+    if ( IsA (expr, Expr) )
+    {
+   List *temp;
+
+   foreach (temp, ((Expr*)expr)->args)
+       if (!exprIsAggOrGroupCol(lfirst(temp),groupClause))
+       return FALSE;
+   return TRUE;
+    }
+
+    return FALSE;
+}
+
 /*
  * tleIsAggOrGroupCol -
  *    returns true if the TargetEntry is Agg or GroupCol.
@@ -2376,9 +2410,9 @@ tleIsAggOrGroupCol(TargetEntry *tle, List *groupClause)
     {
    GroupClause *grpcl = lfirst(gl);
    
-       if ( tle->resdom->resno == grpcl->resdom->resno )
+       if ( tle->resdom->resno == grpcl->entry->resdom->resno )
        {
-           if ( IsA (expr, Aggreg) )
+           if ( contain_agg_clause ((Node*) expr) )
                elog (WARN, "parser: aggregates not allowed in GROUP BY clause");
        return TRUE;
    }
@@ -2387,39 +2421,8 @@ tleIsAggOrGroupCol(TargetEntry *tle, List *groupClause)
     if ( IsA (expr, Aggreg) )
    return TRUE;
 
-    return FALSE;
-}
-
-#if 0  /* Now GroupBy contains resdom to enable Group By func_results */
-/*
- * exprIsAggOrGroupCol -
- *    returns true if the expression does not contain non-group columns.
- */
-static bool
-exprIsAggOrGroupCol(Node *expr, List *groupClause)
-{
-    if (expr==NULL)
-   return TRUE;
-    else if (IsA(expr,Const))
-   return TRUE;
-    else if (IsA(expr,Var)) {
-   List *gl;
-   Var *var = (Var*)expr;
-   /*
-    * only group columns are legal
-    */
-   foreach (gl, groupClause) {
-       GroupClause *grpcl = lfirst(gl);
-       if ((grpcl->grpAttr->varno == var->varno) &&
-       (grpcl->grpAttr->varattno == var->varattno))
-       return TRUE;
-   }
-   return FALSE;
-    } else if (IsA(expr,Aggreg))
-   /* aggregates can take group column or non-group column as argument,
-      no further check necessary. */
-   return TRUE;
-    else if (IsA(expr,Expr)) {
+    if ( IsA (expr, Expr) )
+    {
    List *temp;
 
    foreach (temp, ((Expr*)expr)->args)
@@ -2430,7 +2433,6 @@ exprIsAggOrGroupCol(Node *expr, List *groupClause)
 
     return FALSE;
 }
-#endif
 
 /*
  * parseCheckAggregates -
index 7ae2cd6e9892333715151d4d78e1907b49e990d9..d29e66bcf53935597a925753a3cc9a8028c1ac95 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.14 1997/04/23 05:58:06 vadim Exp $
+ * $Id: parsenodes.h,v 1.15 1997/04/29 04:28:59 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -774,7 +774,7 @@ typedef struct SortClause {
  */
 typedef struct GroupClause {
     NodeTag        type;
-    Resdom     *resdom;    /* attributes to group on */
+    TargetEntry        *entry;     /* attributes to group on */
     Oid            grpOpoid;   /* the sort operator to use */
 } GroupClause;