*** ./src/backend/nodes/copyfuncs.c.orig	2009-05-04 19:54:52.000000000 +0200
--- ./src/backend/nodes/copyfuncs.c	2009-05-04 21:00:33.000000000 +0200
***************
*** 1056,1061 ****
--- 1056,1092 ----
  }
  
  /*
+  * _copyGroupingSetsFunc
+  */
+ static GroupingSetsFunc *
+ _copyGroupingSetsFunc(GroupingSetsFunc *from)
+ {
+ 	GroupingSetsFunc *newnode = makeNode(GroupingSetsFunc);
+ 	
+ 	COPY_SCALAR_FIELD(identity);
+ 	COPY_NODE_FIELD(expr);
+ 	COPY_NODE_FIELD(expr_list);
+ 	COPY_LOCATION_FIELD(location);
+ 	
+ 	return newnode;
+ }
+ 
+ /*
+  * _copyGroupingSetsSpec
+  */
+ static GroupingSetsSpec *
+ _copyGroupingSetsSpec(GroupingSetsSpec *from)
+ {
+ 	GroupingSetsSpec *newnode = makeNode(GroupingSetsSpec);
+ 	
+ 	COPY_NODE_FIELD(set_list);
+ 	COPY_SCALAR_FIELD(has_empty_set);
+ 	COPY_LOCATION_FIELD(location);
+ 	
+ 	return newnode;
+ }
+ 
+ /*
   * _copyScalarArrayOpExpr
   */
  static ScalarArrayOpExpr *
***************
*** 4093,4098 ****
--- 4124,4135 ----
  		case T_XmlSerialize:
  			retval = _copyXmlSerialize(from);
  			break;
+ 		case T_GroupingSetsFunc:
+ 			retval = _copyGroupingSetsFunc(from);
+ 			break;
+ 		case T_GroupingSetsSpec:
+ 			retval = _copyGroupingSetsSpec(from);
+ 			break;
  
  		default:
  			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from));
*** ./src/backend/nodes/equalfuncs.c.orig	2009-05-04 20:01:09.000000000 +0200
--- ./src/backend/nodes/equalfuncs.c	2009-05-04 20:47:27.000000000 +0200
***************
*** 290,295 ****
--- 290,316 ----
  }
  
  static bool
+ _equalGroupingSetsFunc(GroupingSetsFunc *a, GroupingSetsFunc *b)
+ {
+ 	COMPARE_SCALAR_FIELD(identity);
+ 	COMPARE_NODE_FIELD(expr);
+ 	COMPARE_NODE_FIELD(expr_list);
+ 	COMPARE_LOCATION_FIELD(location);
+ 	
+ 	return true;
+ }
+ 
+ static bool
+ _equalGroupingSetsSpec(GroupingSetsSpec *a, GroupingSetsSpec *b)
+ {
+ 	COMPARE_SCALAR_FIELD(set_list);
+ 	COMPARE_SCALAR_FIELD(has_empty_set);
+ 	COMPARE_LOCATION_FIELD(location);
+ 	
+ 	return true;
+ }
+ 
+ static bool
  _equalScalarArrayOpExpr(ScalarArrayOpExpr *a, ScalarArrayOpExpr *b)
  {
  	COMPARE_SCALAR_FIELD(opno);
***************
*** 2871,2876 ****
--- 2892,2903 ----
  		case T_XmlSerialize:
  			retval = _equalXmlSerialize(a, b);
  			break;
+ 		case T_GroupingSetsFunc:
+ 			retval = _equalGroupingSetsFunc(a, b);
+ 			break;
+ 		case T_GroupingSetsSpec:
+ 			retval = _equalGroupingSetsSpec(a, b);
+ 			break;
  
  		default:
  			elog(ERROR, "unrecognized node type: %d",
*** ./src/backend/nodes/nodeFuncs.c.orig	2009-05-04 21:56:15.000000000 +0200
--- ./src/backend/nodes/nodeFuncs.c	2009-05-05 14:05:41.000000000 +0200
***************
*** 917,922 ****
--- 917,928 ----
  			/* just use argument's location */
  			loc = exprLocation((Node *) ((PlaceHolderVar *) expr)->phexpr);
  			break;
+ 		case T_GroupingSetsFunc:
+ 			loc = ((GroupingSetsFunc *) expr)->location;
+ 			break;
+ 		case T_GroupingSetsSpec:
+ 			loc = ((GroupingSetsSpec *) expr)->location;
+ 			break;
  		default:
  			/* for any other node type it's just unknown... */
  			loc = -1;
***************
*** 1342,1347 ****
--- 1348,1368 ----
  			break;
  		case T_PlaceHolderInfo:
  			return walker(((PlaceHolderInfo *) node)->ph_var, context);
+ 		case T_GroupingSetsFunc:
+ 			{
+ 				GroupingSetsFunc *gsf = (GroupingSetsFunc *) node;
+ 				
+ 				if (expression_tree_walker(gsf->expr, walker, context))
+ 					return true;
+ 				if (expression_tree_walker((Node *) gsf->expr_list, walker, context))
+ 					return true;
+ 			}
+ 			break;
+ 		case T_GroupingSetsSpec:
+ 			if (expression_tree_walker((Node *)((GroupingSetsSpec *) node)->set_list,
+ 										walker, context))
+ 					return true;
+ 			break;
  		default:
  			elog(ERROR, "unrecognized node type: %d",
  				 (int) nodeTag(node));
***************
*** 2016,2021 ****
--- 2037,2063 ----
  				return (Node *) newnode;
  			}
  			break;
+ 		case T_GroupingSetsFunc:
+ 			{
+ 				GroupingSetsFunc *gsf = (GroupingSetsFunc *) node;
+ 				GroupingSetsFunc *newnode;
+ 				
+ 				FLATCOPY(newnode, gsf, GroupingSetsFunc);
+ 				MUTATE(newnode->expr, gsf->expr, Node *);
+ 				MUTATE(newnode->expr_list, gsf->expr, List *);
+ 				return (Node *) newnode;
+ 			}
+ 			break;
+ 		case T_GroupingSetsSpec:
+ 			{
+ 				GroupingSetsSpec *gss = (GroupingSetsSpec *) node;
+ 				GroupingSetsSpec *newnode;
+ 				
+ 				FLATCOPY(newnode, gss, GroupingSetsSpec);
+ 				MUTATE(newnode->set_list, gss->set_list, List *);
+ 				return (Node *) newnode;
+ 			}
+ 			break;
  		default:
  			elog(ERROR, "unrecognized node type: %d",
  				 (int) nodeTag(node));
*** ./src/backend/nodes/outfuncs.c.orig	2009-05-04 20:04:05.000000000 +0200
--- ./src/backend/nodes/outfuncs.c	2009-05-04 20:52:47.000000000 +0200
***************
*** 902,907 ****
--- 902,928 ----
  }
  
  static void
+ _outGroupingSetsFunc(StringInfo str, GroupingSetsFunc *node)
+ {
+ 	WRITE_NODE_TYPE("GROUPINGSETSFUNC");
+ 	
+ 	WRITE_ENUM_FIELD(identity, GroupingSetsFuncIdentity);
+ 	WRITE_NODE_FIELD(expr);
+ 	WRITE_NODE_FIELD(expr_list);
+ 	WRITE_LOCATION_FIELD(location);
+ }
+ 
+ static void
+ _outGroupingSetsSpec(StringInfo str, GroupingSetsSpec *node)
+ {
+ 	WRITE_NODE_TYPE("GROUPINGSETSSPEC");
+ 	
+ 	WRITE_NODE_FIELD(set_list);
+ 	WRITE_BOOL_FIELD(has_empty_set);
+ 	WRITE_LOCATION_FIELD(location);
+ }
+ 
+ static void
  _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node)
  {
  	WRITE_NODE_TYPE("SCALARARRAYOPEXPR");
***************
*** 2774,2779 ****
--- 2795,2806 ----
  			case T_XmlSerialize:
  				_outXmlSerialize(str, obj);
  				break;
+ 			case T_GroupingSetsFunc:
+ 				_outGroupingSetsFunc(str, obj);
+ 				break;
+ 			case T_GroupingSetsSpec:
+ 				_outGroupingSetsSpec(str, obj);
+ 				break;
  
  			default:
  
*** ./src/backend/nodes/readfuncs.c.orig	2009-05-04 20:09:32.000000000 +0200
--- ./src/backend/nodes/readfuncs.c	2009-05-04 21:03:35.000000000 +0200
***************
*** 583,588 ****
--- 583,620 ----
  }
  
  /*
+  * _readGroupingSetsFunc
+  */
+ static GroupingSetsFunc *
+ _readGroupingSetsFunc(void)
+ {
+ 	READ_LOCALS(GroupingSetsFunc);
+ 	
+ 	READ_ENUM_FIELD(identity, GroupingSetsFuncIdentity);
+ 	READ_NODE_FIELD(expr);
+ 	READ_NODE_FIELD(expr_list);
+ 	READ_LOCATION_FIELD(location);
+ 	
+ 	READ_DONE();
+ }
+ 
+ /*
+  * _readGroupingSetsSpec
+  */
+ static GroupingSetsSpec *
+ _readGroupingSetsSpec(void)
+ {
+ 	READ_LOCALS(GroupingSetsSpec);
+ 	
+ 	READ_NODE_FIELD(set_list);
+ 	READ_BOOL_FIELD(has_empty_set);
+ 	READ_LOCATION_FIELD(location);
+ 	
+ 	READ_DONE();
+ }
+ 
+ 
+ /*
   * _readScalarArrayOpExpr
   */
  static ScalarArrayOpExpr *
***************
*** 1274,1279 ****
--- 1306,1315 ----
  		return_value = _readNotifyStmt();
  	else if (MATCH("DECLARECURSOR", 13))
  		return_value = _readDeclareCursorStmt();
+ 	else if (MATCH("GROUPINGSETSFUNC", 16))
+ 		return_value = _readGroupingSetsFunc();
+ 	else if (MATCH("GROUPINGSETSSPEC", 16))
+ 		return_value = _readGroupingSetsSpec();
  	else
  	{
  		elog(ERROR, "badly formatted node string \"%.32s\"...", token);
*** ./src/backend/parser/analyze.c.orig	2009-05-05 14:14:42.000000000 +0200
--- ./src/backend/parser/analyze.c	2009-05-12 22:32:12.000000000 +0200
***************
*** 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_relation.h"
  #include "parser/parse_target.h"
***************
*** 148,153 ****
--- 149,300 ----
  }
  
  /*
+  * 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)
+ {
+ 	stmt->groupClause = (List *) expandGroupingSets(pstate, stmt->groupClause);
+ 	
+ 	if (pstate->p_hasGroupingSets)
+ 	{
+ 		GroupingSetsSpec *gss = (GroupingSetsSpec *) stmt->groupClause;
+ 		CommonTableExpr *cte = makeNode(CommonTableExpr);
+ 		SelectStmt  *cteedstmt;
+ 		int	ngroupingsets = list_length(gss->set_list) + (gss->has_empty_set ? 1 : 0);
+ 		
+ 		cteedstmt = makeSelectStmt(NIL, NIL);
+ 							    //list_make1(makeRangeVar(NULL, "**g**", -1)));
+ 
+ 		cteedstmt->intoClause = stmt->intoClause;
+ 		
+ 		cte->ctename = "**g**";
+ 		cte->ctequery = (Node *) stmt;//(Node *) makeSelectStmt( makeStarTargetList(), 
+ 					//		    list_make1(makeRangeVar(NULL, "bagety", -1)));
+ 
+ 		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 = 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 = groupClause;
+ 					rarg->havingClause = copyObject(stmt->havingClause);
+ 					
+ 					setop->op = SETOP_UNION;
+ 					setop->larg = larg;
+ 					setop->rarg = rarg;
+ 					setop->all = true;
+ 					
+ 					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 = true;
+ 					
+ 				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 = NIL;
+ 		return cteedstmt;
+ 	}
+ 
+ 	return stmt;
+ }
+ 
+ /*
   * transformStmt -
   *	  transform a Parse tree into a Query tree.
   */
***************
*** 176,181 ****
--- 323,330 ----
  		case T_SelectStmt:
  			{
  				SelectStmt *n = (SelectStmt *) parseTree;
+ 				
+ 				n = transformGroupingSets(pstate, n);
  
  				if (n->valuesLists)
  					result = transformValuesClause(pstate, n);
***************
*** 829,835 ****
  											stmt->groupClause,
  											&qry->targetList,
  											qry->sortClause,
! 											false);
  
  	if (stmt->distinctClause == NIL)
  	{
--- 978,984 ----
  											stmt->groupClause,
  											&qry->targetList,
  											qry->sortClause,
! 											false); 
  
  	if (stmt->distinctClause == NIL)
  	{
*** ./src/backend/parser/gram.y.orig	2009-05-04 18:07:44.000000000 +0200
--- ./src/backend/parser/gram.y	2009-05-05 15:23:01.000000000 +0200
***************
*** 118,123 ****
--- 118,125 ----
  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);
  static void check_qualified_name(List *names);
  static List *check_func_name(List *names);
***************
*** 416,421 ****
--- 418,426 ----
  %type <str>		opt_existing_window_name
  %type <ival>	opt_frame_clause frame_extent frame_bound
  
+ %type <node>	grouping_element empty_grouping_set grouping_sets_spec
+ %type <list>	grouping_element_list
+ %type <boolean>	opt_grouping_set_quantifier
  
  /*
   * If you make any token changes, update the keyword table in
***************
*** 438,444 ****
  	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
--- 443,449 ----
  	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
***************
*** 451,457 ****
  	FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
  	FREEZE FROM FULL FUNCTION
  
! 	GLOBAL GRANT GRANTED GREATEST GROUP_P
  
  	HANDLER HAVING HEADER_P HOLD HOUR_P
  
--- 456,462 ----
  	FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
  	FREEZE FROM FULL FUNCTION
  
! 	GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING GROUPING_ID
  
  	HANDLER HAVING HEADER_P HOLD HOUR_P
  
***************
*** 485,494 ****
  
  	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
! 	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
--- 490,499 ----
  
  	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
! 	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
***************
*** 7227,7234 ****
  			;
  
  group_clause:
! 			GROUP_P BY expr_list					{ $$ = $3; }
! 			| /*EMPTY*/								{ $$ = NIL; }
  		;
  
  having_clause:
--- 7232,7285 ----
  			;
  
  group_clause:
! 			GROUP_P BY opt_grouping_set_quantifier grouping_element_list		
! 					{ 
! 						if (!$3)
! 							ereport(ERROR,
! 								    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 								     errmsg("GROUP BY DISTINCT isn't supported")));
! 						$$ = $4; 
! 					}
! 			| /*EMPTY*/
! 					{
! 						$$ = NIL; 
! 					}
! 		;
! 
! 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:
! 			'(' ')'
! 				{
! 					$$ = NULL;
! 				}
! 		;
! 
! opt_grouping_set_quantifier:
! 			ALL			{ $$ = true; }
! 			| DISTINCT		{ $$ = false; }
! 			| /*EMPTY*/		{ $$ = true; }
  		;
  
  having_clause:
***************
*** 9266,9271 ****
--- 9317,9345 ----
  					n->location = @1;
  					$$ = (Node *)n;
  				}
+ 			| 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);
+ 				}
  		;
  
  /*
***************
*** 10320,10325 ****
--- 10394,10400 ----
  			| SERVER
  			| SESSION
  			| SET
+ 			| SETS
  			| SHARE
  			| SHOW
  			| SIMPLE
***************
*** 10395,10400 ****
--- 10470,10476 ----
  			| EXTRACT
  			| FLOAT_P
  			| GREATEST
+ 			| GROUPING_ID
  			| INOUT
  			| INT_P
  			| INTEGER
***************
*** 10489,10494 ****
--- 10565,10571 ----
  			| COLUMN
  			| CONSTRAINT
  			| CREATE
+ 			| CUBE
  			| CURRENT_CATALOG
  			| CURRENT_DATE
  			| CURRENT_ROLE
***************
*** 10510,10515 ****
--- 10587,10593 ----
  			| FROM
  			| GRANT
  			| GROUP_P
+ 			| GROUPING
  			| HAVING
  			| IN_P
  			| INITIALLY
***************
*** 10533,10538 ****
--- 10611,10617 ----
  			| PRIMARY
  			| REFERENCES
  			| RETURNING
+ 			| ROLLUP
  			| SELECT
  			| SESSION_USER
  			| SOME
***************
*** 11047,11052 ****
--- 11126,11143 ----
  	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
   */
*** ./src/backend/parser/Makefile.orig	2009-05-04 21:23:31.000000000 +0200
--- ./src/backend/parser/Makefile	2009-05-04 21:24:06.000000000 +0200
***************
*** 14,20 ****
  
  OBJS= analyze.o gram.o keywords.o parser.o parse_agg.o parse_cte.o parse_clause.o \
        parse_expr.o parse_func.o parse_node.o parse_oper.o parse_relation.o \
!       parse_type.o parse_coerce.o parse_target.o parse_utilcmd.o scansup.o kwlookup.o
  
  FLEXFLAGS = -CF
  
--- 14,21 ----
  
  OBJS= analyze.o gram.o keywords.o parser.o parse_agg.o parse_cte.o parse_clause.o \
        parse_expr.o parse_func.o parse_node.o parse_oper.o parse_relation.o \
!       parse_type.o parse_coerce.o parse_target.o parse_utilcmd.o scansup.o kwlookup.o \
!       parse_gsets.o
  
  FLEXFLAGS = -CF
  
*** ./src/backend/parser/parse_agg.c.orig	2009-05-11 21:12:59.000000000 +0200
--- ./src/backend/parser/parse_agg.c	2009-05-12 22:30:37.000000000 +0200
***************
*** 32,42 ****
--- 32,51 ----
  	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 -
***************
*** 318,324 ****
  	 * 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,
--- 327,344 ----
  	 * 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,
***************
*** 426,431 ****
--- 446,517 ----
  }
  
  /*
+  * 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);
+ 			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);
+ 	}
+ 	
+ 	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
*** ./src/backend/parser/parse_expr.c.orig	2009-05-12 22:33:29.000000000 +0200
--- ./src/backend/parser/parse_expr.c	2009-05-12 22:53:56.000000000 +0200
***************
*** 273,278 ****
--- 273,296 ----
  		case T_CurrentOfExpr:
  			result = transformCurrentOfExpr(pstate, (CurrentOfExpr *) 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;
+ 			}
  
  			/*********************************************
  			 * Quietly accept node types that may be presented when we are
*** ./src/backend/parser/parse_gsets.c.orig	2009-05-04 21:13:54.000000000 +0200
--- ./src/backend/parser/parse_gsets.c	2009-05-12 22:12:34.000000000 +0200
***************
*** 0 ****
--- 1,679 ----
+ /*-------------------------------------------------------------------------
+  *
+  * parse_gsets.c
+  *		parser grouping sets transformations
+  *
+  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  $PostgreSQL: $
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ #include "catalog/namespace.h"
+ #include "nodes/nodes.h"
+ #include "nodes/makefuncs.h"
+ #include "nodes/nodeFuncs.h"
+ #include "nodes/value.h"
+ #include "nodes/pg_list.h"
+ #include "nodes/parsenodes.h"
+ #include "optimizer/tlist.h"
+ #include "parser/parse_clause.h"
+ #include "parser/parse_gsets.h"
+ #include "parser/parse_node.h"
+ #include "rewrite/rewriteManip.h"
+ 
+ #include <string.h>
+ 
+ 
+ #define UNDEF_LOCATION	-1
+ 
+ 
+ #define ORDER_CLAUSE 0
+ #define GROUP_CLAUSE 1
+ #define DISTINCT_ON_CLAUSE 2
+ 
+ #define MAX_CUBE_FIELDS		8
+ 
+ #define IsGroupingSetsSpec(n)		(IsA(n, GroupingSetsSpec))
+ #define IsUndefLoc(n)			(n == UNDEF_LOCATION)
+ #define IsNIL(n)			(n == NIL)
+ 
+ typedef struct
+ {
+ 	ParseState	*pstate;
+ 	List		*groupClause;
+ 	bool	isGlobalTL;
+ } transform_ungroup_cols_context;
+ 
+ static List *add_distinct_set(List *gsets_list, List *gsets);
+ static List *add_distinct_sets(List *cum_gsets_list, List *gsets_list);
+ static Node *set_multiplication(List *list, bool has_empty_set);
+ static Node *expandGroupingSetsOperator(ParseState *pstate, Node *node, bool *isemptyset);
+ 
+ 
+ /*
+  * return true, when GroupingSet is empty
+  */
+ static bool
+ IsEmptyGroupingSet(Node *node)
+ {
+ 	if (node == NULL)
+ 		return true;
+ 
+ 	if (IsA(node, GroupingSetsSpec))
+ 		return IsNIL(((GroupingSetsSpec *) node)->set_list);
+ 
+ 	return false;
+ }
+ 
+ /*
+  * expandGroupingSets do:
+  * a) search end replace cube and rollup funccall nodes by GroupingSetsSpec
+  *    nodes,
+  * b) multiply all set in GroupBy list,
+  * c) drop all None nodes.
+  *
+  */
+ Node *
+ expandGroupingSets(ParseState *pstate, List *grouplist)
+ {
+ 	ListCell	*g;
+ 	List	*result = NIL;
+ 	bool		has_empty_set = false;
+ 	int		location = UNDEF_LOCATION;
+ 	bool	is_grouping_sets = false;
+ 
+ 	/* leave fast when grouplist is NIL */
+ 	if (IsNIL(grouplist))
+ 		return NULL;
+ 
+ 	/* detect single group by grouping set */
+ 	foreach(g, grouplist)
+ 	{
+ 		Node *el = (Node *) lfirst(g);
+ 		Node *expel;
+ 		bool	isemptyset;
+ 
+ 		/* 
+ 		 * Is it empty set. NULL is short cut for empty set. 
+ 		 * This has disadvantage - we lost location, but it is short.
+ 		 */
+ 		if (IsEmptyGroupingSet(el))
+ 		{
+ 			has_empty_set = true;
+ 			continue;
+ 		}
+ 		
+ 		/* take location from first item of list */
+ 		if (IsUndefLoc(location))
+ 			location = exprLocation(el);
+ 
+ 		expel = expandGroupingSetsOperator(pstate, el, &isemptyset);
+ 		
+ 		if (isemptyset)
+ 		{
+ 			has_empty_set = true;
+ 			continue;
+ 		}
+ 
+ 		if (IsA(el, GroupingSetsSpec))
+ 			is_grouping_sets = true;
+ 		else
+ 		{
+ 			switch (((GroupingSetsFunc *) el)->identity)
+ 			{
+ 				case FUNCTION_CUBE:
+ 				case FUNCTION_ROLLUP:
+ 					is_grouping_sets = true;
+ 					break;
+ 				default:
+ 					is_grouping_sets = false;
+ 			}
+ 		}
+ 		
+ 		result = lappend(result, expel); 
+ 	}
+ 
+ 	/* 
+ 	 * when isn't detected grouping sets, but is any empty set
+ 	 * do implicit Grouping Set
+ 	 */
+ 	if (!is_grouping_sets)
+ 	{
+ 		if (has_empty_set)
+ 		{
+ 			GroupingSetsSpec *gss = makeNode(GroupingSetsSpec);
+ 			gss->set_list = result;
+ 			gss->has_empty_set = true;
+ 			gss->location = location;
+ 			pstate->p_hasGroupingSets = true;
+ 			
+ 			return (Node *) gss;
+ 		}
+ 		else
+ 			/* when no trick from grouping sets spec is used, return original list */
+ 			return (Node *) grouplist;
+ 	}
+ 
+ 	/* multiple all sets in GROUP BY list */
+ 	pstate->p_hasGroupingSets = true;
+ 	
+ 	return (Node *) set_multiplication(result, has_empty_set);
+ }
+ 
+ /*
+  * This function has some purposes - first take list from row expression,
+  * second - remove all empty sets and remove all duplicate sets,
+  * ToDo: add flag to RowExpr that carry info about explicit ROW keyword 
+  * using. 
+  */
+ static List **
+ adjustElements(List *elements, int *nfields, bool *has_empty_set)
+ {
+ 	ListCell  *l;
+ 	List	*felements = NIL;			/* filtered elements, without empty sets */
+ 	int		_nfields;
+ 	List 		**result;
+ 	int	i = 0;
+ 
+ 	*has_empty_set = false;
+ 	foreach(l, elements)
+ 	{
+ 		if (IsEmptyGroupingSet((Node *) lfirst(l)))
+ 		{
+ 			*has_empty_set = true;
+ 			continue;
+ 		}
+ 		
+ 		felements = list_append_unique(felements, lfirst(l));
+ 	}
+ 	
+ 	if (IsNIL(felements))
+ 		return NULL;
+ 
+ 	_nfields = list_length(felements);
+ 	result = palloc(_nfields * sizeof(List *));
+ 
+ 	foreach(l, felements)
+ 	{
+ 		Node *el = (Node *) lfirst(l);
+ 
+ 		switch (nodeTag(el))
+ 		{
+ 			case T_A_Expr:
+ 			case T_ColumnRef:
+ 				result[i++] = list_make1(el);
+ 				break;
+ 			case T_RowExpr:
+ 				result[i++] = ((RowExpr *) el)->args;
+ 				break;
+ 			default:
+ 				elog(ERROR, "unexpected node type %d", nodeTag(el));
+ 		}
+ 	}
+ 	
+ 	*nfields = _nfields;
+ 	return result;
+ }
+ 
+ /*
+  * This function add one grouping set to other. Result is list of distinct sets, 
+  * etc list of distinct list.
+  *
+  * Note: the distinct in function name is not related to ALL|DISTINCT quantifier
+  * of GROUP BY clause
+  */
+ static List *
+ add_distinct_set(List *gsets_list, List *gsets)
+ {
+ 	
+ 	List	*uniqset = NIL;			/* unique gsets */
+ 	ListCell	*l;
+ 	int	act_len;
+ 	bool	found = false;
+ 
+ 	/* 
+ 	 * ensure so all fields in gsets_list are unique,
+ 	 * all new items are unique there.
+ 	 */
+ 	foreach (l, gsets)
+ 		uniqset = list_append_unique(uniqset, lfirst(l));
+ 
+ 	/* same lists has same length */
+ 	act_len = list_length(uniqset);
+ 
+ 	foreach(l, gsets_list)
+ 	{
+ 		Node *node = (Node *) lfirst(l);
+ 
+ 		Assert(IsA(node, List));
+ 		if (list_length((List *) node) == act_len)
+ 		{
+ 			ListCell *lc;
+ 
+ 			found = true;
+ 			foreach(lc, (List *) node)
+ 				if (!list_member(uniqset, (Node *) lfirst(lc)))
+ 				{
+ 					found = false;
+ 					break;
+ 				}
+ 			if (found)
+ 				break;
+ 		}		
+ 	}		
+ 
+ 	if (!found)
+ 		return lappend(gsets_list, uniqset);
+ 	else
+ 		return gsets_list;	
+ }
+ 
+ /* 
+  * This used in grouping sets(a, rollup(a,b))
+  */
+ static List *
+ add_distinct_sets(List *cum_gsets_list, List *gsets_list)
+ {
+ 	ListCell *l;
+ 	
+ 	foreach(l, gsets_list)
+ 	{
+ 		Node *el = (Node *) lfirst(l);
+ 		Assert(IsA(el, List));
+ 		cum_gsets_list = add_distinct_set(cum_gsets_list, (List *) el);
+ 	}
+ 	return cum_gsets_list;
+ }
+ 
+ /*
+  * Evaluate CUBE and ROLLUP operators
+  *	Transform from GroupingSetsFunc to GroupingSetsSpec. 
+  *
+  * Note: This routine doesn't necessary replace all GroupingSetsFunc nodes. It evaluate only
+  * GROUPING SETS context. There can be any nodes elsewhere - mainly Cube as FuncCall, so
+  * we have to use walker later and check it (and possible replace it).
+  */
+ static Node *
+ expandGroupingSetsOperator(ParseState *pstate, Node *node, bool *isemptyset)
+ {
+ 	bool		has_empty_set = false;
+ 	List	*set_list = NIL;
+ 	GroupingSetsSpec *result;
+ 	int		location;
+ 
+ 	if (IsEmptyGroupingSet(node))
+ 	{
+ 		*isemptyset = true;
+ 		return NULL;
+ 	}
+ 
+ 	*isemptyset = false;
+ 	
+ 	switch (nodeTag(node))
+ 	{
+ 		case T_GroupingSetsFunc:
+ 			{
+ 				GroupingSetsFunc *gsf = (GroupingSetsFunc *) node;
+ 				List	**sets;
+ 				List		*sets_union;
+ 				int	nfields = list_length(gsf->expr_list);
+ 				
+ 				if (gsf->identity == FUNCTION_CUBE && nfields > MAX_CUBE_FIELDS)
+ 					ereport(ERROR,
+ 									(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ 									 errmsg("more than eight fields in CUBE operator"),
+ 									 parser_errposition(pstate, gsf->location)));
+ 
+ 				sets = adjustElements(gsf->expr_list, &nfields, &has_empty_set);
+ 				if (sets == NULL)
+ 				{
+ 					*isemptyset = true;
+ 					return NULL;
+ 				}
+ 				
+ 				switch (gsf->identity)
+ 				{
+ 					case FUNCTION_CUBE:
+ 						{
+ 							/*
+ 							 * Grouping Sets CUBE operator
+ 							 *  This operator evaluate set {a,b} to sets {{a,b},{a},{b},{}}
+ 							 * 
+ 							 * An combinations are generated with bitmap's shift
+ 							 * like
+ 							 *  111   7  {a,b,c}
+ 							 *  110   6  {a,b}
+ 							 *  101   5  {a,c}
+ 							 *  100   4  {a}
+ 							 *  011   3  {b,c}
+ 							 *  010   2  {b}
+ 							 *  001   1  {c}
+ 							 *  000   0  empty set
+ 							 */
+ 							unsigned int btab[] = {0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
+ 							unsigned int bitmap = btab[nfields - 1];
+ 							unsigned int masc = bitmap;
+ 							unsigned int selector = 1 << (nfields - 1);
+ 							int	i;
+ 
+ 							sets_union = NIL;
+ 							while (bitmap > 0)
+ 							{
+ 								unsigned int processed_bitmap = bitmap--;
+ 								
+ 								sets_union = NIL;
+ 								for (i = 0; processed_bitmap > 0; i++)
+ 								{
+ 									if (processed_bitmap & selector)
+ 										sets_union = list_union(sets_union, sets[i]);
+ 									
+ 									processed_bitmap = (processed_bitmap << 1) & masc;
+ 								}
+ 								
+ 								set_list = lappend(set_list, sets_union);
+ 							}
+ 						}
+ 						break;
+ 					
+ 					case FUNCTION_ROLLUP:
+ 						{
+ 							/*
+ 							 * Grouping Sets ROLLUP operator
+ 							 *  This operator evaluate set {a,b} to sets {{a,b},{a},{}}
+ 							 */
+ 							int	i;
+ 							int	j;
+ 							 
+ 							set_list = NIL;
+ 							for (i = 0; i < nfields; i++)
+ 							{
+ 								sets_union = NIL;
+ 								for (j = 0; j < nfields - i; j++)
+ 									sets_union = list_union(sets_union, (List *) sets[j]);
+ 
+ 								set_list = lappend(set_list, sets_union);
+ 							}
+ 						}
+ 						break;
+ 					
+ 					default:
+ 						/* 
+ 						 * none here, for FUNCTION_GROUPING and FUNCTION_GROUPING_ID 
+ 						 * return original node
+ 						 */
+ 						pfree(sets);
+ 						
+ 						return node;
+ 				}
+ 				
+ 				pfree(sets);
+ 				location = gsf->location;
+ 				has_empty_set = true;
+ 			}
+ 			break;
+ 		case T_GroupingSetsSpec:
+ 			{
+ 				/* 
+ 				 * Grouping Sets (xxxx)
+ 				 *  a) call evaluation CUBE and ROLLUP operator
+ 				 *  b) ensure distinct sets
+ 				 *  c) find  empty set inside
+ 				 */
+ 				ListCell	*gse;
+ 				GroupingSetsSpec *gss = (GroupingSetsSpec *) node;
+ 				
+ 				set_list = NIL;
+ 				location = gss->location;
+ 				
+ 				foreach(gse, gss->set_list)
+ 				{
+ 					Node *grouping_element = (Node *) lfirst(gse);
+ 					Node *expanded_set = expandGroupingSetsOperator(pstate, 
+ 											    grouping_element, isemptyset);
+ 
+ 					if (*isemptyset)
+ 					{
+ 						has_empty_set = true;
+ 						continue;
+ 					}
+ 					
+ 					switch (nodeTag(expanded_set))
+ 					{
+ 						case T_GroupingSetsSpec:
+ 							{
+ 								GroupingSetsSpec *egss = (GroupingSetsSpec *) expanded_set;
+ 								
+ 								if (egss->has_empty_set)
+ 									has_empty_set = true;
+ 								
+ 								if (egss->set_list != NIL)
+ 									set_list = add_distinct_sets(set_list, egss->set_list);
+ 							}
+ 							break;
+ 
+ 						case T_RowExpr:
+ 							{
+ 								/* 
+ 								 * parser recognizes in grouping set(a,b,(c,b)) row(c,b),
+ 								 * so is necessary to transform to GroupingSetsSpec
+ 								  */
+ 								ListCell	*l;
+ 								RowExpr *rexpr = (RowExpr *) expanded_set;
+ 		
+ 								foreach(l, rexpr->args)
+ 									if (IsEmptyGroupingSet((Node *)lfirst(l)))
+ 									{
+ 										has_empty_set = true;
+ 										break;
+ 									}
+ 
+ 								set_list = add_distinct_set(set_list, rexpr->args);
+ 							}
+ 							break;
+ 
+ 						default:
+ 							set_list = add_distinct_set(set_list, list_make1(expanded_set));
+ 					}
+ 				}
+ 			}
+ 			break;
+ 			
+ 		default:
+ 			return node;
+ 	}
+ 	
+ 	if (set_list == NIL)
+ 	{
+ 		*isemptyset = true;
+ 		return NULL;
+ 	}
+ 
+ 	result = (GroupingSetsSpec *) makeNode(GroupingSetsSpec);
+ 	result->has_empty_set = has_empty_set;
+ 	result->set_list = set_list;
+ 	result->location = location;
+ 	/* this grouping set isn't empty */
+ 	*isemptyset = false;
+ 	
+ 	return (Node *) result;
+ }
+ 
+ /* multiple two nodes:
+  * GroupingSetsSpec x GroupingSetsSpec 
+  * Node * Node
+  * Node * GroupingSetsSpec
+  * GroupingSetsSpec * Node
+  */
+ static Node *
+ multiple(Node *a, Node *b)
+ {
+ 	ListCell	*l;
+ 	ListCell	*lj;
+ 	List		*set_list = NIL;
+ 	GroupingSetsSpec	*result;
+ 	GroupingSetsSpec	*gss_a;
+ 	GroupingSetsSpec	*gss_b;
+ 
+ 	/* leave when nodes are equal */
+ 	if (equal(a, b))
+ 		return a;
+ 
+ 	if (!IsGroupingSetsSpec(a) && !IsGroupingSetsSpec(b))
+ 	{
+ 		GroupingSetsSpec *gss = (GroupingSetsSpec *) makeNode(GroupingSetsSpec);
+ 		gss->set_list = list_make1(list_make2(a,b));
+ 		gss->has_empty_set = false;
+ 		gss->location = exprLocation(a);
+ 
+ 		return (Node *) gss;
+ 	}
+ 	if (IsGroupingSetsSpec(a) && !IsGroupingSetsSpec(b))
+ 	{
+ 		Node *aux = a;
+ 		
+ 		a = b; b = aux;
+ 	}
+ 	if (!IsGroupingSetsSpec(a) && IsGroupingSetsSpec(b))
+ 	{
+ 		gss_b = (GroupingSetsSpec *) b;
+ 		
+ 		foreach(l, gss_b->set_list)
+ 		{
+ 			List *set = (List *) lfirst(l);
+ 
+ 			Assert(IsA(lfirst(l), List));
+ 
+ 			/* add item to all grouping sets */
+ 			set = list_append_unique(set, a);
+ 			set_list = add_distinct_set(set_list, set);
+ 		}
+ 
+ 		if (gss_b->has_empty_set)
+ 		{
+ 			set_list = add_distinct_set(set_list, (list_make1(a)));
+ 			gss_b->has_empty_set = false;
+ 		}
+ 
+ 		gss_b->set_list = set_list;
+ 		
+ 		return (Node *) b;
+ 	}
+ 
+ 	/*
+ 	 * ((A,B),(C)) * ((X,Y),()) = ((A,B,X,Y),(A,B),(C,X,Y),(C))
+          */
+ 	Assert(IsGroupingSetsSpec(a) && IsGroupingSetsSpec(b));
+ 	
+ 	gss_a = (GroupingSetsSpec *) a;
+ 	gss_b = (GroupingSetsSpec *) b;
+ 
+ 	result = (GroupingSetsSpec *) makeNode(GroupingSetsSpec);
+ 
+ 	foreach(l, gss_a->set_list)
+ 	{
+ 		foreach(lj, gss_b->set_list)
+ 			set_list = add_distinct_set(set_list, 
+ 					list_union((List *) lfirst(l),
+ 						   (List *) lfirst(lj)));
+ 		if  (gss_b->has_empty_set)
+ 			set_list = add_distinct_set(set_list, 
+ 						    (List *) lfirst(l));
+ 	}
+ 
+ 	if (gss_a->has_empty_set)
+ 		foreach(l, gss_b->set_list)
+ 			set_list = add_distinct_set(set_list, (List *) lfirst(l)); 
+ 
+ 	result->set_list = set_list;
+ 	result->has_empty_set = gss_a->has_empty_set && gss_b->has_empty_set;
+ 	result->location = gss_a->location;
+ 
+ 	return (Node *) result;
+ }
+ 
+ /*
+  * multiply elements of list -> (a1,a2,a2,a4) => (((a1 x a2) x a3) x a4)
+  */
+ static Node *
+ set_multiplication(List *list, bool has_empty_set)
+ {
+ 	int	nfields = list_length(list);
+ 	ListCell	*lc;
+ 	bool	first_iter = true;
+ 	Node	*stack = NULL;
+ 
+ 	foreach(lc, list)
+ 	{
+ 		Node *el = (Node *) lfirst(lc);
+ 
+ 		/* Propagate info about empty set to first item */
+ 		if (first_iter)
+ 		{
+ 			if (has_empty_set)
+ 			{
+ 				if (IsGroupingSetsSpec(el))
+ 				{
+ 					((GroupingSetsSpec *) el)->has_empty_set = true;
+ 					/* leave when list has only one field */
+ 					if (nfields == 1)
+ 						return el;
+ 					
+ 					stack = el;
+ 				}
+ 				else
+ 				{
+ 					GroupingSetsSpec *gss = makeNode(GroupingSetsSpec);
+ 					gss->set_list = list_make1(list_make1(el));
+ 					gss->has_empty_set = true;
+ 					gss->location = exprLocation(el);
+ 				
+ 					stack = (Node *) gss;
+ 
+ 					/* leave when list has only one field */
+ 					if (nfields == 1)
+ 						return stack;
+ 				}
+ 			}
+ 			else
+ 				stack = el;
+ 
+ 			first_iter = false;
+ 			/* go to for next item */
+ 			continue;
+ 		}
+ 
+ 		Assert(stack != NULL);
+ 		stack = multiple(stack, el);
+ 	}
+ 
+ 	return stack;
+ }
+ 
+ /*
+  * This procedure prepare global targetList, local targetLists and local
+  * groupClauses.
+  */
+ List *
+ transformGroupingSetsSpec(ParseState *pstate, List *groupClause,
+ 						List **groupClauses, List **targetLists,
+ 						List **targetList, List *sortClause)
+ {
+ 	List	*globalTL = copyObject((Node *)*targetList);
+ 	List	*globalGC = NIL;
+ 	List	*gpcs = NIL;
+ 	List	*tls = NIL;
+ 	ListCell	*gsc;
+ 	GroupingSetsSpec *gsspec = (GroupingSetsSpec *) linitial(groupClause);
+ 
+ 	*groupClauses = gpcs;
+ 	*targetLists = tls;
+ 	*targetList = globalTL;
+ 
+ 	return globalGC;
+ }
+ 
*** ./src/backend/parser/parse_target.c.orig	2009-05-04 21:16:40.000000000 +0200
--- ./src/backend/parser/parse_target.c	2009-05-04 21:22:30.000000000 +0200
***************
*** 1446,1451 ****
--- 1446,1467 ----
  		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;
  	}
*** ./src/include/nodes/nodes.h.orig	2009-05-04 19:32:41.000000000 +0200
--- ./src/include/nodes/nodes.h	2009-05-04 20:46:20.000000000 +0200
***************
*** 376,381 ****
--- 376,383 ----
  	T_XmlSerialize,
  	T_WithClause,
  	T_CommonTableExpr,
+ 	T_GroupingSetsFunc,
+ 	T_GroupingSetsSpec,
  
  	/*
  	 * TAGS FOR RANDOM OTHER STUFF
*** ./src/include/nodes/parsenodes.h.orig	2009-05-04 19:15:28.000000000 +0200
--- ./src/include/nodes/parsenodes.h	2009-05-04 20:47:42.000000000 +0200
***************
*** 374,379 ****
--- 374,390 ----
  } 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;
+ 
+ /*
   * WindowDef - raw representation of WINDOW and OVER clauses
   *
   * For entries in a WINDOW list, "name" is the window name being defined.
***************
*** 416,421 ****
--- 427,452 ----
  	 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
*** ./src/include/parser/kwlist.h.orig	2009-05-04 18:08:32.000000000 +0200
--- ./src/include/parser/kwlist.h	2009-05-05 14:44:39.000000000 +0200
***************
*** 98,103 ****
--- 98,104 ----
  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)
***************
*** 168,173 ****
--- 169,176 ----
  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)
***************
*** 317,322 ****
--- 320,326 ----
  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)
***************
*** 334,339 ****
--- 338,344 ----
  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)
*** ./src/include/parser/parse_gsets.h.orig	2009-05-04 21:41:52.000000000 +0200
--- ./src/include/parser/parse_gsets.h	2009-05-11 11:22:57.000000000 +0200
***************
*** 0 ****
--- 1,14 ----
+ #include "parser/parse_node.h"
+ #include "nodes/pg_list.h"
+ 
+ 
+ #ifndef PARSE_GETS_H
+ #define PARSE_GETS_H
+ 
+ Node *expandGroupingSets(ParseState *pstate, List *grouplist);
+ List *transformGroupingSetsSpec(ParseState *pstate, List *groupClause,
+ 						List **groupClauses, List **targetLists,
+ 						List **targetList, List *sortClause);
+ 
+ 
+ #endif	/* PARSE_GETS_H */
*** ./src/include/parser/parse_node.h.orig	2009-05-11 20:31:38.000000000 +0200
--- ./src/include/parser/parse_node.h	2009-05-11 20:32:05.000000000 +0200
***************
*** 101,106 ****
--- 101,107 ----
  	bool		p_is_insert;
  	bool		p_is_update;
  	Relation	p_target_relation;
+ 	bool		p_hasGroupingSets;
  	RangeTblEntry *p_target_rangetblentry;
  } ParseState;
  
