In case anyone's interested, I've taken the CTE-based grouping sets patch from
[1] and made it apply to 9.1, attached. I haven't yet done things like checked
it for whitespace consistency, style conformity, or anything else, but (tuits
permitting) hope to figure out how it works and get it closer to commitability
in some upcoming commitfest.

I mention it here so that if someone else is working on it, we can avoid
duplicated effort, and to see if a CTE-based grouping sets implementation is
really the way we think we want to go.

[1] http://archives.postgresql.org/pgsql-hackers/2009-05/msg00700.php

--
Joshua Tolley / eggyknap
End Point Corporation
http://www.endpoint.com
diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile
index a8f4c07..fb248a6 100644
*** a/src/backend/parser/Makefile
--- b/src/backend/parser/Makefile
*************** override CPPFLAGS := -I. -I$(srcdir) $(C
*** 15,21 ****
  OBJS= analyze.o gram.o keywords.o kwlookup.o parser.o \
        parse_agg.o parse_clause.o parse_coerce.o parse_cte.o parse_expr.o \
        parse_func.o parse_node.o parse_oper.o parse_param.o parse_relation.o \
!       parse_target.o parse_type.o parse_utilcmd.o scansup.o
  
  FLEXFLAGS = -CF
  
--- 15,21 ----
  OBJS= analyze.o gram.o keywords.o kwlookup.o parser.o \
        parse_agg.o parse_clause.o parse_coerce.o parse_cte.o parse_expr.o \
        parse_func.o parse_node.o parse_oper.o parse_param.o parse_relation.o \
!       parse_target.o parse_type.o parse_utilcmd.o scansup.o parse_gsets.o
  
  FLEXFLAGS = -CF
  
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 6b99a10..1b579a8 100644
*** a/src/backend/parser/analyze.c
--- b/src/backend/parser/analyze.c
***************
*** 34,39 ****
--- 34,40 ----
  #include "parser/parse_clause.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_cte.h"
+ #include "parser/parse_gsets.h"
  #include "parser/parse_oper.h"
  #include "parser/parse_param.h"
  #include "parser/parse_relation.h"
*************** parse_sub_analyze(Node *parseTree, Parse
*** 150,155 ****
--- 151,313 ----
  }
  
  /*
+  * process GROUPING SETS
+  */
+ static SelectStmt *
+ makeSelectStmt(List *targetList, List *fromClause)
+ {
+ 	SelectStmt *n = makeNode(SelectStmt);
+ 	n->distinctClause = NULL;
+ 	n->intoClause = NULL;
+ 	n->targetList = targetList;
+ 	n->fromClause = fromClause;
+ 	n->whereClause = NULL;
+ 	n->groupClause = NULL;
+ 	n->havingClause = NULL;
+ 	n->windowClause = NIL;
+ 	n->withClause = NULL;
+ 	n->valuesLists = NIL;
+ 	n->sortClause = NIL;
+ 	n->limitOffset = NULL;
+ 	n->limitCount = NULL;
+ 	n->lockingClause = NIL;
+ 	n->op = SETOP_NONE;
+ 	n->all = false;
+ 	n->larg = NULL;
+ 	n->rarg = NULL;
+ 	return n;
+ }
+ 
+ static List *
+ makeStarTargetList(void)
+ {
+ 	ResTarget *rt = makeNode(ResTarget);
+ 	
+ 	rt->name = NULL;
+ 	rt->indirection = NIL;
+ 	rt->val = (Node *) makeNode(ColumnRef);
+ 	((ColumnRef *) rt->val)->fields = list_make1(makeNode(A_Star));
+ 	rt->location = -1;
+ 
+ 	return list_make1(rt);
+ }
+  
+ static SelectStmt *
+ transformGroupingSets(ParseState *pstate, SelectStmt *stmt)
+ {
+ 	if (stmt->groupClause && IsA(stmt->groupClause, GroupByClause))
+ 	{
+ 		GroupingSetsSpec *gss = (GroupingSetsSpec *) expandGroupingSets(pstate, 
+ 						(List *)((GroupByClause *)stmt->groupClause)->fields);
+ 	
+ 		if (pstate->p_hasGroupingSets)
+ 		{
+ 			CommonTableExpr *cte = makeNode(CommonTableExpr);
+ 			SelectStmt  *cteedstmt;
+ 			int	ngroupingsets = list_length(gss->set_list) + (gss->has_empty_set ? 1 : 0);
+ 			bool	all = ((GroupByClause *) stmt->groupClause)->all;
+ 		
+ 			cteedstmt = makeSelectStmt(NIL, NIL);
+ 			cteedstmt->intoClause = stmt->intoClause;
+ 			cteedstmt->sortClause = stmt->sortClause;
+ 			cteedstmt->limitOffset = stmt->limitOffset;
+ 			cteedstmt->limitCount = stmt->limitCount;
+ 			cteedstmt->lockingClause = stmt->lockingClause;
+ 		
+ 			cte->ctename = "**g**";
+ 			cte->ctequery = (Node *) stmt;
+ 			cte->location = -1;
+ 		
+ 			cteedstmt->withClause = makeNode(WithClause);
+ 			cteedstmt->withClause->ctes = list_make1(cte);
+ 			cteedstmt->withClause->recursive = false;
+ 			cteedstmt->withClause->location = -1;
+ 		
+ 			/* when is more than one grouping set, then we should generate setop node */
+ 			if (ngroupingsets > 1)
+ 			{
+ 				/* add quuery under union all for every grouping set */
+ 				SelectStmt	*larg = NULL;
+ 				SelectStmt	*rarg;
+ 				ListCell    *lc;
+ 			
+ 				foreach(lc, gss->set_list)
+ 				{
+ 					List	*groupClause;
+ 				
+ 					Assert(IsA(lfirst(lc), List));
+ 					groupClause = (List *) lfirst(lc);
+ 				
+ 					if (larg == NULL)
+ 					{
+ 						larg = makeSelectStmt(copyObject(stmt->targetList),
+ 									list_make1(makeRangeVar(NULL, "**g**", -1)));
+ 						larg->groupClause = (Node *) groupClause;
+ 						larg->havingClause = copyObject(stmt->havingClause);
+ 					}
+ 					else
+ 					{
+ 						SelectStmt	*setop = makeSelectStmt(NIL, NIL);
+ 					
+ 						rarg = makeSelectStmt(copyObject(stmt->targetList),
+ 									list_make1(makeRangeVar(NULL, "**g**", -1)));
+ 						rarg->groupClause = (Node *) groupClause;
+ 						rarg->havingClause = copyObject(stmt->havingClause);
+ 					
+ 						setop->op = SETOP_UNION;
+ 						setop->larg = larg;
+ 						setop->rarg = rarg;
+ 						setop->all = all;
+ 					
+ 						larg = setop;
+ 					}
+ 				}
+ 				if (gss->has_empty_set)
+ 				{
+ 					SelectStmt	*setop = makeSelectStmt(NIL, NIL);
+ 				
+ 					rarg = makeSelectStmt(copyObject(stmt->targetList),
+ 								list_make1(makeRangeVar(NULL, "**g**", -1)));
+ 					rarg->havingClause = copyObject(stmt->havingClause);
+ 				
+ 					setop->op = SETOP_UNION;
+ 					setop->larg = larg;
+ 					setop->rarg = rarg;
+ 					setop->all = all;
+ 					
+ 					larg = setop;
+ 				}
+ 				/* merge larg to result */
+ 				cteedstmt->op = larg->op;
+ 				cteedstmt->larg = larg->larg;
+ 				cteedstmt->rarg = larg->rarg;
+ 				cteedstmt->all = larg->all;
+ 			}
+ 			else
+ 			{
+ 				/* there isn't used setop node */
+ 				cteedstmt->targetList = copyObject(stmt->targetList);
+ 				cteedstmt->fromClause = list_make1(makeRangeVar(NULL, "**g**", -1));
+ 			}
+ 		
+ 			((SelectStmt *)cte->ctequery)->targetList = makeStarTargetList();
+ 			((SelectStmt *)cte->ctequery)->groupClause = NULL;
+ 			((SelectStmt *)cte->ctequery)->sortClause = NIL;
+ 			((SelectStmt *)cte->ctequery)->limitOffset = stmt->limitOffset;
+ 			((SelectStmt *)cte->ctequery)->limitCount = stmt->limitCount;
+ 			((SelectStmt *)cte->ctequery)->lockingClause = stmt->lockingClause;
+ 		
+ 			return cteedstmt;
+ 		}
+ 		else
+ 			/* trim GroupByClause to groupByClause */
+ 			stmt->groupClause = (Node *)((GroupByClause *)stmt->groupClause)->fields;
+ 	}
+ 
+ 	return stmt;
+ }
+ 
+ /*
   * transformStmt -
   *	  transform a Parse tree into a Query tree.
   */
*************** transformStmt(ParseState *pstate, Node *
*** 179,184 ****
--- 337,344 ----
  			{
  				SelectStmt *n = (SelectStmt *) parseTree;
  
+   				n = transformGroupingSets(pstate, n);
+ 
  				if (n->valuesLists)
  					result = transformValuesClause(pstate, n);
  				else if (n->op == SETOP_NONE)
*************** transformSelectStmt(ParseState *pstate, 
*** 827,833 ****
  										  false /* allow SQL92 rules */ );
  
  	qry->groupClause = transformGroupClause(pstate,
! 											stmt->groupClause,
  											&qry->targetList,
  											qry->sortClause,
  											false /* allow SQL92 rules */ );
--- 987,993 ----
  										  false /* allow SQL92 rules */ );
  
  	qry->groupClause = transformGroupClause(pstate,
! 											(List *) stmt->groupClause,
  											&qry->targetList,
  											qry->sortClause,
  											false /* allow SQL92 rules */ );
*************** transformValuesClause(ParseState *pstate
*** 924,930 ****
  	Assert(stmt->targetList == NIL);
  	Assert(stmt->fromClause == NIL);
  	Assert(stmt->whereClause == NULL);
! 	Assert(stmt->groupClause == NIL);
  	Assert(stmt->havingClause == NULL);
  	Assert(stmt->windowClause == NIL);
  	Assert(stmt->op == SETOP_NONE);
--- 1084,1090 ----
  	Assert(stmt->targetList == NIL);
  	Assert(stmt->fromClause == NIL);
  	Assert(stmt->whereClause == NULL);
! 	Assert(stmt->groupClause == NULL);
  	Assert(stmt->havingClause == NULL);
  	Assert(stmt->windowClause == NIL);
  	Assert(stmt->op == SETOP_NONE);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3f6eeeb..1d907c5 100644
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static Node *makeBitStringConst(char *st
*** 111,116 ****
--- 111,118 ----
  static Node *makeNullAConst(int location);
  static Node *makeAConst(Value *v, int location);
  static Node *makeBoolAConst(bool state, int location);
+ static Node *makeGroupingSetsFunc(GroupingSetsFuncIdentity identity, Node *expr, 
+ 							List *expr_list, int location);
  static FuncCall *makeOverlaps(List *largs, List *rargs,
  							  int location, core_yyscan_t yyscanner);
  static void check_qualified_name(List *names, core_yyscan_t yyscanner);
*************** static TypeName *TableFuncTypeName(List 
*** 292,298 ****
  				target_list insert_column_list set_target_list
  				set_clause_list set_clause multiple_set_clause
  				ctext_expr_list ctext_row def_list indirection opt_indirection
! 				reloption_list group_clause TriggerFuncArgs select_limit
  				opt_select_limit opclass_item_list opclass_drop_list
  				opt_opfamily transaction_mode_list_or_empty
  				TableFuncElementList opt_type_modifiers
--- 294,300 ----
  				target_list insert_column_list set_target_list
  				set_clause_list set_clause multiple_set_clause
  				ctext_expr_list ctext_row def_list indirection opt_indirection
!   				reloption_list TriggerFuncArgs select_limit
  				opt_select_limit opclass_item_list opclass_drop_list
  				opt_opfamily transaction_mode_list_or_empty
  				TableFuncElementList opt_type_modifiers
*************** static TypeName *TableFuncTypeName(List 
*** 437,442 ****
--- 439,449 ----
  				opt_frame_clause frame_extent frame_bound
  %type <str>		opt_existing_window_name
  
+ %type <node>	grouping_element empty_grouping_set grouping_sets_spec
+ 				group_clause
+ %type <list>	grouping_element_list
+ %type <boolean>	opt_grouping_set_quantifier
+   
  
  /*
   * Non-keyword token types.  These are hard-wired into the "flex" lexer.
*************** static TypeName *TableFuncTypeName(List 
*** 472,478 ****
  	COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
  	CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB
  	CREATEROLE CREATEUSER CROSS CSV CURRENT_P
! 	CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
  	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
  
  	DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
--- 479,485 ----
  	COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
  	CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB
  	CREATEROLE CREATEUSER CROSS CSV CURRENT_P
! 	CUBE CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
  	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
  
  	DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
*************** static TypeName *TableFuncTypeName(List 
*** 485,491 ****
  	FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
  	FREEZE FROM FULL FUNCTION FUNCTIONS
  
! 	GLOBAL GRANT GRANTED GREATEST GROUP_P
  
  	HANDLER HAVING HEADER_P HOLD HOUR_P
  
--- 492,498 ----
  	FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
  	FREEZE FROM FULL FUNCTION FUNCTIONS
  
! 	GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING GROUPING_ID
  
  	HANDLER HAVING HEADER_P HOLD HOUR_P
  
*************** static TypeName *TableFuncTypeName(List 
*** 519,528 ****
  
  	RANGE READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REINDEX
  	RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
! 	RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
  
  	SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
! 	SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE
  	SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE_P START STATEMENT
  	STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P
  	SYMMETRIC SYSID SYSTEM_P
--- 526,535 ----
  
  	RANGE READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REINDEX
  	RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
! 	RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP ROW ROWS RULE
  
  	SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
! 	SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SETS SHARE
  	SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE_P START STATEMENT
  	STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P
  	SYMMETRIC SYSID SYSTEM_P
*************** first_or_next: FIRST_P								{ $$ = 0; 
*** 7764,7772 ****
  
  
  group_clause:
! 			GROUP_P BY expr_list					{ $$ = $3; }
! 			| /*EMPTY*/								{ $$ = NIL; }
! 		;
  
  having_clause:
  			HAVING a_expr							{ $$ = $2; }
--- 7771,7825 ----
  
  
  group_clause:
!   			GROUP_P BY opt_grouping_set_quantifier grouping_element_list		
!   					{ 
!   						GroupByClause *clause = makeNode(GroupByClause);
!   						clause->all = $3;
!   						clause->fields = $4;
!   						clause->location = @1;
!   						$$ = (Node *) clause; 
!   					}
!   			| /*EMPTY*/
!   					{
!   						$$ = NULL; 
!   					}
!   		;
!   
! grouping_sets_spec:
!   			GROUPING SETS '(' grouping_element_list ')'
!   				{
!   					/* We cannot identify and drop empty sets yet. */
!   					GroupingSetsSpec *gss = makeNode(GroupingSetsSpec);
!   					gss->set_list = $4;
!   					gss->has_empty_set = false;
!   					gss->location = @1;
!   					$$ = (Node *) gss;
!   				}
!   		;
!   		
! grouping_element_list:
!   			grouping_element							{ $$ = list_make1($1); }
!   			| grouping_element_list ',' grouping_element			{ $$ = lappend($1, $3); }
!   		;
!   		
! grouping_element:
!   			a_expr							{ $$ = $1; }
!   			| grouping_sets_spec					{ $$ = $1; }
!   			| empty_grouping_set					{ $$ = $1; }
!   		;
!   
! empty_grouping_set:
!   			'(' ')'
!   				{
!   					$$ = makeNode(EmptySet);
!   				}
!   		;
!   
! opt_grouping_set_quantifier:
!   			ALL			{ $$ = true; }
!   			| DISTINCT		{ $$ = false; }
!   			| /*EMPTY*/		{ $$ = true; }
!   		;
  
  having_clause:
  			HAVING a_expr							{ $$ = $2; }
*************** c_expr:		columnref								{ $$ = $1; }
*** 9293,9299 ****
  					r->location = @1;
  					$$ = (Node *)r;
  				}
! 		;
  
  /*
   * func_expr is split out from c_expr just so that we have a classification
--- 9346,9375 ----
  					r->location = @1;
  					$$ = (Node *)r;
  				}
!   			| GROUPING '(' a_expr ')'
!   				{
!   					$$ = makeGroupingSetsFunc(FUNCTION_GROUPING, $3, NIL, @1);
!   				}
!   			| GROUPING_ID '(' expr_list ')'
!   				{
!   					$$ = makeGroupingSetsFunc(FUNCTION_GROUPING_ID, NULL, $3, @1);
!   				}
!   			| CUBE '(' expr_list ')'
!   				{
!   					/* 
!   					 * Cube should be used in two different contexts. First,
!   					 * as part of Grouping Sets specification. Second, as
!   					 * normal UDF function from contrib cube module. When isnot 
!   					 * grouping sets context, then node is transformated to 
!   					 * FuncCall node later.
!   					 */
!   					 $$ = makeGroupingSetsFunc(FUNCTION_CUBE, NULL, $3, @1);
!   				}
!   			| ROLLUP '(' expr_list ')'
!   				{
!   					$$ = makeGroupingSetsFunc(FUNCTION_ROLLUP, NULL, $3, @1);
!   				}
!   		;
  
  /*
   * func_expr is split out from c_expr just so that we have a classification
*************** unreserved_keyword:
*** 11010,11015 ****
--- 11086,11092 ----
  			| SERVER
  			| SESSION
  			| SET
+ 			| SETS
  			| SHARE
  			| SHOW
  			| SIMPLE
*************** col_name_keyword:
*** 11087,11092 ****
--- 11164,11170 ----
  			| EXTRACT
  			| FLOAT_P
  			| GREATEST
+             | GROUPING_ID
  			| INOUT
  			| INT_P
  			| INTEGER
*************** reserved_keyword:
*** 11181,11186 ****
--- 11259,11265 ----
  			| COLUMN
  			| CONSTRAINT
  			| CREATE
+             | CUBE
  			| CURRENT_CATALOG
  			| CURRENT_DATE
  			| CURRENT_ROLE
*************** reserved_keyword:
*** 11202,11207 ****
--- 11281,11287 ----
  			| FROM
  			| GRANT
  			| GROUP_P
+             | GROUPING
  			| HAVING
  			| IN_P
  			| INITIALLY
*************** reserved_keyword:
*** 11223,11228 ****
--- 11303,11309 ----
  			| PRIMARY
  			| REFERENCES
  			| RETURNING
+             | ROLLUP
  			| SELECT
  			| SESSION_USER
  			| SOME
*************** makeXmlExpr(XmlExprOp op, char *name, Li
*** 11728,11733 ****
--- 11809,11826 ----
  	return (Node *) x;
  }
  
+ static Node *
+ makeGroupingSetsFunc(GroupingSetsFuncIdentity identity, Node *expr, List *expr_list, int location)
+ {
+ 	GroupingSetsFunc *gsfunc = makeNode(GroupingSetsFunc);
+ 	
+ 	gsfunc->identity = identity;
+ 	gsfunc->expr = expr;
+ 	gsfunc->expr_list = expr_list;
+ 	gsfunc->location = location;
+ 	return (Node *) gsfunc;
+ }
+ 
  /* parser_init()
   * Initialize to parse one query string
   */
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 0a69bde..3aeea45 100644
*** a/src/backend/parser/parse_agg.c
--- b/src/backend/parser/parse_agg.c
*************** typedef struct
*** 33,44 ****
--- 33,52 ----
  	bool		have_non_var_grouping;
  	int			sublevels_up;
  } check_ungrouped_columns_context;
+   
+ typedef struct
+ {
+ 	ParseState *pstate;
+ 	List		*groupClause;
+ } transform_ungrouped_target_context;
  
  static void check_ungrouped_columns(Node *node, ParseState *pstate,
  						List *groupClauses, bool have_non_var_grouping);
  static bool check_ungrouped_columns_walker(Node *node,
  							   check_ungrouped_columns_context *context);
  
+ static Node * transform_ungrouped_target(Node *node, ParseState *pstate,
+ 							List *groupClauses);
  
  /*
   * transformAggregateCall -
*************** parseCheckAggregates(ParseState *pstate,
*** 405,411 ****
  	 * WINDOW clauses.	For that matter, it's also going to examine the
  	 * grouping expressions themselves --- but they'll all pass the test ...
  	 */
! 	clause = (Node *) qry->targetList;
  	if (hasJoinRTEs)
  		clause = flatten_join_alias_vars(root, clause);
  	check_ungrouped_columns(clause, pstate,
--- 413,428 ----
  	 * WINDOW clauses.	For that matter, it's also going to examine the
  	 * grouping expressions themselves --- but they'll all pass the test ...
  	 */
!   	if (pstate->parentParseState && pstate->parentParseState->p_hasGroupingSets)
!   	{
!   		clause = (Node *) transform_ungrouped_target((Node *) qry->targetList, 
!   										    pstate, 
!   										    groupClauses);
!   		/* HACK!!! - move to transform part*/
!   		qry->targetList = clause;
!   	}
!   	else
!   		clause = (Node *) qry->targetList;
  	if (hasJoinRTEs)
  		clause = flatten_join_alias_vars(root, clause);
  	check_ungrouped_columns(clause, pstate,
*************** parseCheckWindowFuncs(ParseState *pstate
*** 514,519 ****
--- 531,613 ----
  }
  
  /*
+  * transform_ungrouped_cols_mutator -
+  *   All non aggregates non constatnt columns are replaced by NULL,
+  *   grouping and grouping_id functions are replaced by constatnts.
+  */
+ static Node *
+ transform_ungrouped_target_mutator(Node *node,
+ 					     transform_ungrouped_target_context *context)
+ {
+ 	if (node == NULL)
+ 		return NULL;
+ 	if (IsA(node, Aggref))
+ 		return node;
+ 
+ 	if (IsA(node, GroupingSetsFunc))
+ 	{
+ 		GroupingSetsFunc *gsf = (GroupingSetsFunc *) node;
+ 		int	result = 0;
+ 		
+ 		if (gsf->identity == FUNCTION_GROUPING)
+ 		{
+ 			result = list_member(context->groupClause, gsf->expr) ? 0 : 1;
+ 			return (Node *) make_const(context->pstate, makeInteger(result), gsf->location);
+ 		}
+ 		else if (gsf->identity == FUNCTION_GROUPING_ID)
+ 		{
+ 			ListCell	*el;
+ 			
+ 			foreach(el, gsf->expr_list)
+ 			{
+ 				result = result << 1;
+ 				if (!list_member(context->groupClause, lfirst(el)))
+ 					result = result | 0x01;
+ 			}
+ 			return (Node *) make_const(context->pstate, makeInteger(result), gsf->location);
+ 		}
+ 		else /* replace Cube and Rollup by FuncCall node */
+ 		{
+ 			/* ToDo: Support cube function */
+ 		}
+ 	}
+ 	
+ 	if (IsA(node, Var))
+ 	{
+ 		Var *var = (Var *) node;
+ 
+ 		if (list_member(context->groupClause, node))
+ 			return node;
+ 		else
+ 			return (Node *) makeNullConst(var->vartype, var->vartypmod);
+ 	}
+ 	else if (IsA(node, FuncExpr))
+ 	{
+ 		FuncExpr *fexpr = (FuncExpr *) node;
+ 		
+ 		if (list_member(context->groupClause, node))
+ 			return node;
+ 		else
+ 			return (Node *) makeNullConst(fexpr->funcresulttype, -1);
+ 	}
+ 	
+ 	return expression_tree_mutator(node, 
+ 					    transform_ungrouped_target_mutator, context);
+ }
+ 
+ static Node *
+ transform_ungrouped_target(Node *node, ParseState *pstate,
+ 							List *groupClauses)
+ {
+ 	transform_ungrouped_target_context context;
+ 	
+ 	context.pstate = pstate;
+ 	context.groupClause = groupClauses;
+ 	
+ 	return transform_ungrouped_target_mutator(node, &context);
+ }
+ 
+ /*
   * check_ungrouped_columns -
   *	  Scan the given expression tree for ungrouped variables (variables
   *	  that are not listed in the groupClauses list and are not within
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 888b526..72762b1 100644
*** a/src/backend/parser/parse_expr.c
--- b/src/backend/parser/parse_expr.c
*************** transformExpr(ParseState *pstate, Node *
*** 288,293 ****
--- 288,312 ----
  		case T_BooleanTest:
  			result = transformBooleanTest(pstate, (BooleanTest *) expr);
  			break;
+  		
+  		case T_GroupingSetsFunc:
+  			{
+  				GroupingSetsFunc *gsf = (GroupingSetsFunc *) expr;
+  				ListCell	*lc;
+  				List		*expr_list = NIL;
+  				
+  				gsf->expr = (Node *) transformExpr(pstate, (Node *) gsf->expr);
+  
+  				foreach(lc, gsf->expr_list)
+  				{
+  					expr_list = lappend(expr_list, transformExpr(pstate,
+  											 (Node *) lfirst(lc)));
+  				}
+  				gsf->expr_list = expr_list;
+  				result = expr;
+  				break;
+  			}
+   
  
  		case T_CurrentOfExpr:
  			result = transformCurrentOfExpr(pstate, (CurrentOfExpr *) expr);
*************** transformExpr(ParseState *pstate, Node *
*** 324,329 ****
--- 343,349 ----
  		case T_CoerceToDomain:
  		case T_CoerceToDomainValue:
  		case T_SetToDefault:
+         case T_EmptySet:
  			{
  				result = (Node *) expr;
  				break;
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index e542dc0..1a3e969 100644
*** a/src/backend/parser/parse_target.c
--- b/src/backend/parser/parse_target.c
*************** FigureColnameInternal(Node *node, char *
*** 1586,1591 ****
--- 1586,1607 ----
  		case T_XmlSerialize:
  			*name = "xmlserialize";
  			return 2;
+  		case T_GroupingSetsFunc:
+  			switch (((GroupingSetsFunc *) node)->identity)
+  			{
+  				case FUNCTION_GROUPING:
+  					*name = "grouping";
+  					return 2;
+  				case FUNCTION_GROUPING_ID:
+  					*name = "grouping_id";
+  					return 2;
+  				case FUNCTION_CUBE:		/* by compiler quite */
+  				case FUNCTION_ROLLUP:
+  					/* nothing */
+  					break;
+  			}
+  			break;
+  			
  		default:
  			break;
  	}
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index a5f5df5..836e38d 100644
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 385,390 ****
--- 385,394 ----
  	T_XmlSerialize,
  	T_WithClause,
  	T_CommonTableExpr,
+ 	T_GroupingSetsFunc,
+ 	T_GroupingSetsSpec,
+ 	T_EmptySet,
+ 	T_GroupByClause,
  
  	/*
  	 * TAGS FOR RANDOM OTHER STUFF
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b591073..9efa9c7 100644
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct SortBy
*** 378,383 ****
--- 378,410 ----
  } SortBy;
  
  /*
+  * GroupingSetsSpec - for GROUP BY GROUPING SETS clause
+  */
+ typedef struct GroupingSetsSpec
+ {
+ 	NodeTag		type;
+ 	List	    *set_list;
+ 	bool		has_empty_set; /* true when grouping sets contains empty set */
+ 	int			location;
+ } GroupingSetsSpec;
+ 
+ typedef struct EmptySet
+ {
+ 	NodeTag		type;
+ } EmptySet;
+ 
+ /*
+  * GroupByClause for GROUP BY clause
+  */
+ typedef struct GroupByClause
+ {
+ 	NodeTag		type;
+ 	bool	all;
+ 	List		*fields;
+ 	int			location;
+ } GroupByClause;
+ 
+ /*
   * WindowDef - raw representation of WINDOW and OVER clauses
   *
   * For entries in a WINDOW list, "name" is the window name being defined.
*************** typedef struct WindowDef
*** 431,436 ****
--- 458,483 ----
  	 FRAMEOPTION_END_CURRENT_ROW)
  
  /*
+  * GroupingSetsFunc - parser node for Grouping, Grouping_id, Cube and Rollup quasy functions
+  */
+ typedef enum GroupingSetsFuncIdentity
+ {
+ 	FUNCTION_GROUPING,
+ 	FUNCTION_GROUPING_ID,
+ 	FUNCTION_CUBE,
+ 	FUNCTION_ROLLUP
+ } GroupingSetsFuncIdentity;
+ 
+ typedef struct GroupingSetsFunc
+ {
+ 	NodeTag		type;
+ 	GroupingSetsFuncIdentity	identity;
+ 	Node			*expr;
+ 	List			*expr_list;
+ 	int		location;
+ } GroupingSetsFunc;
+ 
+ /*
   * RangeSubselect - subquery appearing in a FROM clause
   */
  typedef struct RangeSubselect
*************** typedef struct SelectStmt
*** 956,962 ****
  	List	   *targetList;		/* the target list (of ResTarget) */
  	List	   *fromClause;		/* the FROM clause */
  	Node	   *whereClause;	/* WHERE qualification */
! 	List	   *groupClause;	/* GROUP BY clauses */
  	Node	   *havingClause;	/* HAVING conditional-expression */
  	List	   *windowClause;	/* WINDOW window_name AS (...), ... */
  	WithClause *withClause;		/* WITH clause */
--- 1003,1009 ----
  	List	   *targetList;		/* the target list (of ResTarget) */
  	List	   *fromClause;		/* the FROM clause */
  	Node	   *whereClause;	/* WHERE qualification */
! 	Node	   *groupClause;	/* GROUP BY clauses */
  	Node	   *havingClause;	/* HAVING conditional-expression */
  	List	   *windowClause;	/* WINDOW window_name AS (...), ... */
  	WithClause *withClause;		/* WITH clause */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 49d4b6c..d0dcfe7 100644
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
*************** PG_KEYWORD("createrole", CREATEROLE, UNR
*** 99,104 ****
--- 99,105 ----
  PG_KEYWORD("createuser", CREATEUSER, UNRESERVED_KEYWORD)
  PG_KEYWORD("cross", CROSS, TYPE_FUNC_NAME_KEYWORD)
  PG_KEYWORD("csv", CSV, UNRESERVED_KEYWORD)
+ PG_KEYWORD("cube", CUBE, RESERVED_KEYWORD)
  PG_KEYWORD("current", CURRENT_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("current_catalog", CURRENT_CATALOG, RESERVED_KEYWORD)
  PG_KEYWORD("current_date", CURRENT_DATE, RESERVED_KEYWORD)
*************** PG_KEYWORD("grant", GRANT, RESERVED_KEYW
*** 171,176 ****
--- 172,179 ----
  PG_KEYWORD("granted", GRANTED, UNRESERVED_KEYWORD)
  PG_KEYWORD("greatest", GREATEST, COL_NAME_KEYWORD)
  PG_KEYWORD("group", GROUP_P, RESERVED_KEYWORD)
+ PG_KEYWORD("grouping", GROUPING, RESERVED_KEYWORD)
+ PG_KEYWORD("grouping_id", GROUPING_ID, COL_NAME_KEYWORD)
  PG_KEYWORD("handler", HANDLER, UNRESERVED_KEYWORD)
  PG_KEYWORD("having", HAVING, RESERVED_KEYWORD)
  PG_KEYWORD("header", HEADER_P, UNRESERVED_KEYWORD)
*************** PG_KEYWORD("revoke", REVOKE, UNRESERVED_
*** 318,323 ****
--- 321,327 ----
  PG_KEYWORD("right", RIGHT, TYPE_FUNC_NAME_KEYWORD)
  PG_KEYWORD("role", ROLE, UNRESERVED_KEYWORD)
  PG_KEYWORD("rollback", ROLLBACK, UNRESERVED_KEYWORD)
+ PG_KEYWORD("rollup", ROLLUP, RESERVED_KEYWORD)
  PG_KEYWORD("row", ROW, COL_NAME_KEYWORD)
  PG_KEYWORD("rows", ROWS, UNRESERVED_KEYWORD)
  PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD)
*************** PG_KEYWORD("session", SESSION, UNRESERVE
*** 336,341 ****
--- 340,346 ----
  PG_KEYWORD("session_user", SESSION_USER, RESERVED_KEYWORD)
  PG_KEYWORD("set", SET, UNRESERVED_KEYWORD)
  PG_KEYWORD("setof", SETOF, COL_NAME_KEYWORD)
+ PG_KEYWORD("sets", SETS, UNRESERVED_KEYWORD)
  PG_KEYWORD("share", SHARE, UNRESERVED_KEYWORD)
  PG_KEYWORD("show", SHOW, UNRESERVED_KEYWORD)
  PG_KEYWORD("similar", SIMILAR, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 1c1383b..f2b80bd 100644
*** a/src/include/parser/parse_node.h
--- b/src/include/parser/parse_node.h
*************** struct ParseState
*** 107,112 ****
--- 107,113 ----
  	bool		p_is_update;
  	bool		p_locked_from_parent;
  	Relation	p_target_relation;
+     bool        p_hasGroupingSets;
  	RangeTblEntry *p_target_rangetblentry;
  
  	/*

Attachment: signature.asc
Description: Digital signature

Reply via email to