From a1fbd470a9ae934bf2316740c06b0cf88607e6e8 Mon Sep 17 00:00:00 2001
From: "Vadim B. Mikheev" <vadim4o@yahoo.com>
Date: Tue, 29 Apr 1997 04:32:50 +0000
Subject: [PATCH] Fix GroupBy: enable functions over aggregates and GroupBy-ed
 fields in target list.

---
 src/backend/optimizer/plan/planmain.c | 71 +++++++++++++++---------
 src/backend/parser/analyze.c          | 78 ++++++++++++++-------------
 src/include/nodes/parsenodes.h        |  4 +-
 3 files changed, 87 insertions(+), 66 deletions(-)

diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index f94552d372..7f70d76ac6 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -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;
     
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 8fc78d3aca..1bc64047d6 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -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 -
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 7ae2cd6e98..d29e66bcf5 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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;
 
-- 
2.24.1