grouping sets - updated patch

Started by Pavel Stehuleover 15 years ago3 messages
#1Pavel Stehule
pavel.stehule@gmail.com
1 attachment(s)

Hello

I fixed an issues with empty sets. It just work, but there are some ugly hacks.

It's really needs own planner node - now grouping functions are not
supported by ORDER BY clause.

Regards

Pavel Stehule

Attachments:

verze003.difftext/x-patch; charset=US-ASCII; name=verze003.diffDownload
*** ./src/backend/nodes/copyfuncs.c.orig	2010-08-09 15:34:35.448223379 +0200
--- ./src/backend/nodes/copyfuncs.c	2010-08-09 15:35:01.905222827 +0200
***************
*** 2089,2094 ****
--- 2089,2141 ----
  	return newnode;
  }
  
+ static GroupBy *
+ _copyGroupBy(GroupBy *from)
+ {
+ 	GroupBy   *newnode = makeNode(GroupBy);
+ 	
+ 	COPY_SCALAR_FIELD(all);
+ 	COPY_NODE_FIELD(grouping_sets);
+ 	COPY_LOCATION_FIELD(location);
+ 	
+ 	return newnode;
+ }
+ 
+ static GroupingSetsFunction *
+ _copyGroupingSetsFunction(GroupingSetsFunction *from)
+ {
+ 	GroupingSetsFunction	  *newnode = makeNode(GroupingSetsFunction);
+ 
+ 	COPY_SCALAR_FIELD(kind);
+ 	COPY_NODE_FIELD(expr);
+ 	COPY_NODE_FIELD(expr_list);
+ 	COPY_LOCATION_FIELD(location);
+ 	
+ 	return newnode;
+ }
+ 
+ static GroupingSetsSpecification *
+ _copyGroupingSetsSpecification(GroupingSetsSpecification *from)
+ {
+ 	GroupingSetsSpecification *newnode = makeNode(GroupingSetsSpecification);
+ 	
+ 	COPY_NODE_FIELD(grouping_sets);
+ 	COPY_SCALAR_FIELD(has_empty_set);
+ 	COPY_LOCATION_FIELD(location);
+ 	
+ 	return newnode;
+ }
+ 
+ static GroupingSetsEmptySet *
+ _copyGroupingSetsEmptySet(GroupingSetsEmptySet *from)
+ {
+ 	GroupingSetsEmptySet *newnode = makeNode(GroupingSetsEmptySet);
+ 
+ 	COPY_LOCATION_FIELD(location);
+ 	
+ 	return newnode;
+ }
+ 
  static WindowDef *
  _copyWindowDef(WindowDef *from)
  {
***************
*** 4208,4213 ****
--- 4255,4272 ----
  		case T_SortBy:
  			retval = _copySortBy(from);
  			break;
+ 		case T_GroupBy:
+ 			retval = _copyGroupBy(from);
+ 			break;
+ 		case T_GroupingSetsFunction:
+ 			retval = _copyGroupingSetsFunction(from);
+ 			break;
+ 		case T_GroupingSetsSpecification:
+ 			retval = _copyGroupingSetsSpecification(from);
+ 			break;
+ 		case T_GroupingSetsEmptySet:
+ 			retval = _copyGroupingSetsEmptySet(from);
+ 			break;
  		case T_WindowDef:
  			retval = _copyWindowDef(from);
  			break;
*** ./src/backend/nodes/equalfuncs.c.orig	2010-08-09 15:34:35.450099758 +0200
--- ./src/backend/nodes/equalfuncs.c	2010-08-09 15:35:01.908222800 +0200
***************
*** 2052,2057 ****
--- 2052,2096 ----
  }
  
  static bool
+ _equalGroupBy(GroupBy *a, GroupBy *b)
+ {
+ 	COMPARE_SCALAR_FIELD(all);
+ 	COMPARE_NODE_FIELD(grouping_sets);
+ 	COMPARE_LOCATION_FIELD(location);
+ 	
+ 	return true;
+ }
+ 
+ static bool
+ _equalGroupingSetsFunction(GroupingSetsFunction *a, GroupingSetsFunction *b)
+ {
+ 	COMPARE_SCALAR_FIELD(kind);
+ 	COMPARE_NODE_FIELD(expr);
+ 	COMPARE_NODE_FIELD(expr_list);
+ 	COMPARE_LOCATION_FIELD(location);
+ 	
+ 	return true;
+ }
+ 
+ static bool
+ _equalGroupingSetsSpecification(GroupingSetsSpecification *a, GroupingSetsSpecification *b)
+ {
+ 	COMPARE_NODE_FIELD(grouping_sets);
+ 	COMPARE_SCALAR_FIELD(has_empty_set);
+ 	COMPARE_LOCATION_FIELD(location);
+ 	
+ 	return true;
+ }
+ 
+ static bool
+ _equalGroupingSetsEmptySet(GroupingSetsEmptySet *a, GroupingSetsEmptySet *b)
+ {
+ 	COMPARE_LOCATION_FIELD(location);
+ 	
+ 	return true;
+ }
+ 
+ static bool
  _equalWindowDef(WindowDef *a, WindowDef *b)
  {
  	COMPARE_STRING_FIELD(name);
***************
*** 2875,2880 ****
--- 2914,2931 ----
  		case T_SortBy:
  			retval = _equalSortBy(a, b);
  			break;
+ 		case T_GroupBy:
+ 			retval = _equalGroupBy(a, b);
+ 			break;
+ 		case T_GroupingSetsFunction:
+ 			retval = _equalGroupingSetsFunction(a, b);
+ 			break;
+ 		case T_GroupingSetsSpecification:
+ 			retval = _equalGroupingSetsSpecification(a, b);
+ 			break;
+ 		case T_GroupingSetsEmptySet:
+ 			retval = _equalGroupingSetsEmptySet(a, b);
+ 			break;
  		case T_WindowDef:
  			retval = _equalWindowDef(a, b);
  			break;
*** ./src/backend/nodes/nodeFuncs.c.orig	2010-02-12 18:33:20.000000000 +0100
--- ./src/backend/nodes/nodeFuncs.c	2010-08-09 20:53:15.953222162 +0200
***************
*** 223,228 ****
--- 223,231 ----
  		case T_PlaceHolderVar:
  			type = exprType((Node *) ((PlaceHolderVar *) expr)->phexpr);
  			break;
+ 		case T_GroupingSetsFunction:
+ 			type = INT4OID;
+ 			break;
  		default:
  			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
  			type = InvalidOid;	/* keep compiler quiet */
***************
*** 1373,1378 ****
--- 1376,1401 ----
  			break;
  		case T_PlaceHolderInfo:
  			return walker(((PlaceHolderInfo *) node)->ph_var, context);
+ 		case T_GroupingSetsFunction:
+ 			{
+ 				GroupingSetsFunction *gsf = (GroupingSetsFunction *) node;
+ 
+ 	    			if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING)
+ 	    			{
+ 	    				if (expression_tree_walker((Node *) gsf->expr,
+ 	    									   walker, context))
+ 	    					return true;
+ 	    			}
+ 	    			else if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING_ID)
+ 	    			{
+ 	    				if (expression_tree_walker((Node *) gsf->expr_list,
+ 	    									   walker, context))
+ 	    					return true;
+ 	    			}
+ 			}
+ 			break;
+ 		
+ 			
  		default:
  			elog(ERROR, "unrecognized node type: %d",
  				 (int) nodeTag(node));
***************
*** 2062,2068 ****
--- 2085,2108 ----
  				return (Node *) newnode;
  			}
  			break;
+ 		case T_GroupingSetsFunction:
+ 			{
+ 				GroupingSetsFunction *gsf = (GroupingSetsFunction *) node;
+ 				GroupingSetsFunction *newnode;
+ 				
+ 				FLATCOPY(newnode, gsf, GroupingSetsFunction);
+ 				
+ 				if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING)
+ 					MUTATE(newnode->expr, gsf->expr, Expr *);
+ 				else if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING_ID)
+ 					MUTATE(newnode->expr_list, gsf->expr_list, List *);
+ 				
+ 				return (Node *) newnode;
+ 			}
+ 			break;
  		default:
+ 		Assert(false);
+ 		elog_node_display(ERROR, "wrong node", node, true);
  			elog(ERROR, "unrecognized node type: %d",
  				 (int) nodeTag(node));
  			break;
*** ./src/backend/nodes/outfuncs.c.orig	2010-08-09 15:34:35.451097798 +0200
--- ./src/backend/nodes/outfuncs.c	2010-08-09 15:35:01.915227556 +0200
***************
*** 2327,2332 ****
--- 2327,2371 ----
  }
  
  static void
+ _outGroupBy(StringInfo str, GroupBy *node)
+ {
+ 	WRITE_NODE_TYPE("GROUPBY");
+ 	
+ 	WRITE_BOOL_FIELD(all);
+ 	WRITE_NODE_FIELD(grouping_sets);
+ 	WRITE_LOCATION_FIELD(location);
+ }
+ 
+ static void
+ _outGroupingSetsFunction(StringInfo str, GroupingSetsFunction *node)
+ {
+ 	WRITE_NODE_TYPE("GS_FUNCTION");
+ 	
+ 	WRITE_ENUM_FIELD(kind, GroupingSets_func_kind);
+ 	WRITE_NODE_FIELD(expr);
+ 	WRITE_NODE_FIELD(expr_list);
+ 	WRITE_LOCATION_FIELD(location);
+ }
+ 
+ static void
+ _outGroupingSetsSpecification(StringInfo str, GroupingSetsSpecification *node)
+ {
+ 	WRITE_NODE_TYPE("GS_SPECIFICATION");
+ 	
+ 	WRITE_NODE_FIELD(grouping_sets);
+ 	WRITE_BOOL_FIELD(has_empty_set);
+ 	WRITE_LOCATION_FIELD(location);
+ }
+ 
+ static void 
+ _outGroupingSetsEmptySet(StringInfo str, GroupingSetsEmptySet *node)
+ {
+ 	WRITE_NODE_TYPE("GS_EMPTY_SET");
+ 	
+ 	WRITE_LOCATION_FIELD(location);
+ }
+ 
+ static void
  _outWindowDef(StringInfo str, WindowDef *node)
  {
  	WRITE_NODE_TYPE("WINDOWDEF");
***************
*** 2881,2886 ****
--- 2920,2937 ----
  			case T_SortBy:
  				_outSortBy(str, obj);
  				break;
+ 			case T_GroupBy:
+ 				_outGroupBy(str, obj);
+ 				break;
+ 			case T_GroupingSetsFunction:
+ 				_outGroupingSetsFunction(str, obj);
+ 				break;
+ 			case T_GroupingSetsSpecification:
+ 				_outGroupingSetsSpecification(str, obj);
+ 				break;
+ 			case T_GroupingSetsEmptySet:
+ 				_outGroupingSetsEmptySet(str, obj);
+ 				break;
  			case T_WindowDef:
  				_outWindowDef(str, obj);
  				break;
*** ./src/backend/nodes/readfuncs.c.orig	2010-08-09 15:34:35.453099666 +0200
--- ./src/backend/nodes/readfuncs.c	2010-08-09 15:35:01.917222582 +0200
***************
*** 268,273 ****
--- 268,323 ----
  }
  
  /*
+  * _readGroupBy
+  */
+ static GroupBy *
+ _readGroupBy(void)
+ {
+ 	READ_LOCALS(GroupBy);
+ 	
+ 	READ_BOOL_FIELD(all);
+ 	READ_NODE_FIELD(grouping_sets);
+ 	READ_LOCATION_FIELD(location);
+ 	
+ 	READ_DONE();
+ }
+ 
+ static GroupingSetsFunction *
+ _readGroupingSetsFunction(void)
+ {
+ 	READ_LOCALS(GroupingSetsFunction);
+ 	
+ 	READ_ENUM_FIELD(kind, GroupingSets_func_kind);
+ 	READ_NODE_FIELD(expr);
+ 	READ_NODE_FIELD(expr_list);
+ 	READ_LOCATION_FIELD(location);
+ 	
+ 	READ_DONE();
+ }
+ 
+ static GroupingSetsSpecification *
+ _readGroupingSetsSpecification(void)
+ {
+ 	READ_LOCALS(GroupingSetsSpecification);
+ 
+ 	READ_NODE_FIELD(grouping_sets);
+ 	READ_BOOL_FIELD(has_empty_set);
+ 	READ_LOCATION_FIELD(location);
+ 	
+ 	READ_DONE();
+ }
+ 
+ static GroupingSetsEmptySet *
+ _readGroupingSetsEmptySet(void)
+ {
+ 	READ_LOCALS(GroupingSetsEmptySet);
+ 	
+ 	READ_LOCATION_FIELD(location);
+ 	
+ 	READ_DONE();
+ }
+ 
+ /*
   * _readWindowClause
   */
  static WindowClause *
***************
*** 1200,1205 ****
--- 1250,1263 ----
  		return_value = _readQuery();
  	else if (MATCH("SORTGROUPCLAUSE", 15))
  		return_value = _readSortGroupClause();
+ 	else if (MATCH("GROUPBY", 7))
+ 		return_value = _readGroupBy();
+ 	else if (MATCH("GS_FUNCTION", 11))
+ 		return_value = _readGroupingSetsFunction();
+ 	else if (MATCH("GS_SPECIFICATION", 16))
+ 		return_value = _readGroupingSetsSpecification();
+ 	else if (MATCH("GS_EMPTY_SET", 12))
+ 		return_value = _readGroupingSetsEmptySet();
  	else if (MATCH("WINDOWCLAUSE", 12))
  		return_value = _readWindowClause();
  	else if (MATCH("ROWMARKCLAUSE", 13))
*** ./src/backend/optimizer/util/clauses.c.orig	2010-03-19 23:54:41.000000000 +0100
--- ./src/backend/optimizer/util/clauses.c	2010-08-09 22:39:23.144222535 +0200
***************
*** 2970,2975 ****
--- 2970,2995 ----
  		return eval_const_expressions_mutator((Node *) phv->phexpr,
  											  context);
  	}
+ 	if (IsA(node, GroupingSetsFunction))
+ 	{
+ 		GroupingSetsFunction *gsf = (GroupingSetsFunction *) node;
+ 		
+ 		if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING)
+ 		{
+ 			Datum 		result = Int32GetDatum(1);
+ 			int16		typLen;
+ 			bool		typByVal;
+ 			Datum		pval;
+ 			
+ 			get_typlenbyval(INT4OID, &typLen, &typByVal);
+ 			return (Node *) makeConst(INT4OID,
+ 								 -1,
+ 								 (int) typLen,
+ 								 result,
+ 								 false,
+ 								 typByVal);
+ 		}
+ 	}
  
  	/*
  	 * For any node type not handled above, we recurse using
*** ./src/backend/parser/analyze.c.orig	2010-02-26 03:00:49.000000000 +0100
--- ./src/backend/parser/analyze.c	2010-08-09 22:38:52.369097509 +0200
***************
*** 29,34 ****
--- 29,35 ----
  #include "nodes/makefuncs.h"
  #include "nodes/nodeFuncs.h"
  #include "optimizer/var.h"
+ #include "parser/parser.h"
  #include "parser/analyze.h"
  #include "parser/parse_agg.h"
  #include "parser/parse_clause.h"
***************
*** 63,68 ****
--- 64,75 ----
  					 ExplainStmt *stmt);
  static void transformLockingClause(ParseState *pstate, Query *qry,
  					   LockingClause *lc, bool pushedDown);
+ static SelectStmt *transformGroupingSets(ParseState *pstate, SelectStmt *stmt);
+ 
+ extern Node *transform_ungrouped_target(Node *node, ParseState *pstate, List *groupClauses);
+ 
+ Node *replace_groupingsets_functions_mutator(Node *node,
+ 							    void *context);
  
  
  /*
***************
*** 178,183 ****
--- 185,192 ----
  		case T_SelectStmt:
  			{
  				SelectStmt *n = (SelectStmt *) parseTree;
+ 				
+ 				n = transformGroupingSets(pstate, n);
  
  				if (n->valuesLists)
  					result = transformValuesClause(pstate, n);
***************
*** 766,771 ****
--- 775,803 ----
  	return result;
  }
  
+ Node *
+ replace_groupingsets_functions_mutator(Node *node,
+ 							    void *context)
+ {
+ 	if (node == NULL)
+ 		return NULL;
+ 	if (IsA(node, Query))
+ 	{
+ 		return (Node *)query_tree_mutator((Query*) node,
+ 							replace_groupingsets_functions_mutator,
+ 							(void *) context,
+ 							0);
+ 	}
+ 	
+ 	if (IsA(node, GroupingSetsFunction))
+ 	{
+ 	}
+ 	
+ 	return expression_tree_mutator(node,
+ 						replace_groupingsets_functions_mutator,
+ 						(void *) context);
+ }
+ 
  
  /*
   * transformSelectStmt -
***************
*** 826,833 ****
  										  true /* fix unknowns */ ,
  										  false /* allow SQL92 rules */ );
  
  	qry->groupClause = transformGroupClause(pstate,
! 											stmt->groupClause,
  											&qry->targetList,
  											qry->sortClause,
  											false /* allow SQL92 rules */ );
--- 858,871 ----
  										  true /* fix unknowns */ ,
  										  false /* allow SQL92 rules */ );
  
+ 	/*
+ 	 * In this moment grouping sets are transformed, so stmt->groupClause,
+ 	 * have to be a list or NULL
+ 	 */
+ 	Assert(stmt->groupClause == NULL || IsA(stmt->groupClause, List));
+ 
  	qry->groupClause = transformGroupClause(pstate,
! 											(List *) stmt->groupClause,
  											&qry->targetList,
  											qry->sortClause,
  											false /* allow SQL92 rules */ );
***************
*** 892,897 ****
--- 930,938 ----
  							   (LockingClause *) lfirst(l), false);
  	}
  
+ 
+ 	qry = replace_groupingsets_functions_mutator(qry, qry->groupClause);
+ 
  	return qry;
  }
  
***************
*** 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);
--- 965,971 ----
  	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);
***************
*** 2241,2243 ****
--- 2282,2492 ----
  	rc->pushedDown = pushedDown;
  	qry->rowMarks = lappend(qry->rowMarks, rc);
  }
+ 
+ 
+ /*
+  * generate parser's node for simple SELECT
+  */
+ 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;
+ }
+ 
+ /*
+  * generate parser's node for star target
+  */
+ 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);
+ }
+ 
+ /*
+  * used for empty grouping set to ensure GroupAggregate
+  */
+ static Node *
+ makeFakeGroupByClause(void)
+ {
+ 	A_Const *n = makeNode(A_Const);
+ 	TypeCast *t = makeNode(TypeCast);
+ 
+ 	n->val.type = T_String;
+ 	n->val.val.str = "t";
+ 	n->location = -1;
+ 	
+ 	t->arg = (Node *) n;
+ 	t->typeName = SystemTypeName("bool");
+ 	t->location = -1;
+ 	return (Node *) list_make1(t);
+ }
+ 
+ 
+ /*
+  * transform a GROUP BY GROUPING SETS to CTE
+  */
+ static SelectStmt *
+ transformGroupingSets(ParseState *pstate, SelectStmt *stmt)
+ {
+ 	if (stmt->groupClause && IsA(stmt->groupClause, GroupBy))
+ 	{
+ 		GroupingSetsSpecification *gss = (GroupingSetsSpecification *) evalGroupingSets(pstate, 
+ 													(List *)((GroupBy *)stmt->groupClause)->grouping_sets);
+ 	
+ 		if (pstate->p_hasGroupingSets)
+ 		{
+ 			CommonTableExpr *cte = makeNode(CommonTableExpr);
+ 			SelectStmt  *cteedstmt;
+ 			int	ngroupingsets = list_length(gss->grouping_sets) + (gss->has_empty_set ? 1 : 0);
+ 			bool	all = ((GroupBy *) 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 = "GroupingSets";
+ 			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->grouping_sets)
+ 				{
+ 					List	*groupClause;
+ 				
+ 					Assert(IsA(lfirst(lc), List));
+ 					groupClause = (List *) lfirst(lc);
+ 				
+ 					if (larg == NULL)
+ 					{
+ 						larg = makeSelectStmt(copyObject(stmt->targetList),
+ 									list_make1(makeRangeVar(NULL, "GroupingSets", -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, "GroupingSets", -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)
+ 				{
+ 				
+ 					/* 
+ 					 * there is big hack,only for this WIP version,
+ 					 * Now we don't know if modified target list will contain
+ 					 * a aggregate or not. But we have to ensure a aggregate
+ 					 * mode - and then we use a simply trick, use a GROUP BY by "true"
+ 					 */
+ 					SelectStmt	*setop = makeSelectStmt(NIL, NIL);
+ 				
+ 					rarg = makeSelectStmt(copyObject(stmt->targetList),
+ 								list_make1(makeRangeVar(NULL, "GroupingSets", -1)));
+ 					rarg->havingClause = copyObject(stmt->havingClause);
+ 					rarg->groupClause = makeFakeGroupByClause();
+ 				
+ 					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 if (ngroupingsets == 1)
+ 			{
+ 				/* there isn't used setop node */
+ 				cteedstmt->targetList = copyObject(stmt->targetList);
+ 				cteedstmt->fromClause = list_make1(makeRangeVar(NULL, "GroupingSets", -1));
+ 				cteedstmt->havingClause = copyObject(stmt->havingClause);
+ 				
+ 				if (gss->grouping_sets != NIL)
+ 				{
+ 					cteedstmt->groupClause = linitial(gss->grouping_sets);
+ 					
+ 				}
+ 				else
+ 				{
+ 					cteedstmt->groupClause = makeFakeGroupByClause();
+ 				}
+ 			}
+ 		
+ 			((SelectStmt *)cte->ctequery)->targetList = makeStarTargetList();
+ 			((SelectStmt *)cte->ctequery)->groupClause = NULL;
+ 			((SelectStmt *)cte->ctequery)->havingClause = 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 *)((GroupBy *)stmt->groupClause)->grouping_sets;
+ 	}
+ 
+ 	return stmt;
+ }
*** ./src/backend/parser/gram.y.orig	2010-08-09 15:34:35.456099145 +0200
--- ./src/backend/parser/gram.y	2010-08-09 17:12:42.521222576 +0200
***************
*** 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
--- 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 TriggerFuncArgs select_limit
  				opt_select_limit opclass_item_list opclass_drop_list
  				opt_opfamily transaction_mode_list_or_empty
  				TableFuncElementList opt_type_modifiers
***************
*** 437,442 ****
--- 437,446 ----
  				opt_frame_clause frame_extent frame_bound
  %type <str>		opt_existing_window_name
  
+ %type <node>	gsets_element gsets_empty_set gsets_specification group_clause
+ %type <list>	gsets_element_list
+ %type <boolean>	opt_gsets_quantifier
+ 
  
  /*
   * Non-keyword token types.  These are hard-wired into the "flex" lexer.
***************
*** 471,477 ****
  	CLUSTER COALESCE COLLATE COLUMN COMMENT COMMENTS COMMIT
  	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
  
--- 475,481 ----
  	CLUSTER COALESCE COLLATE COLUMN COMMENT COMMENTS COMMIT
  	COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
  	CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB
! 	CREATEROLE CREATEUSER CROSS CSV CUBE CURRENT_P
  	CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
  	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
  
***************
*** 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
  
--- 489,495 ----
  	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
  
***************
*** 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
--- 523,532 ----
  
  	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
***************
*** 7795,7804 ****
  			| NEXT									{ $$ = 0; }
  		;
  
- 
  group_clause:
! 			GROUP_P BY expr_list					{ $$ = $3; }
! 			| /*EMPTY*/								{ $$ = NIL; }
  		;
  
  having_clause:
--- 7799,7851 ----
  			| NEXT									{ $$ = 0; }
  		;
  
  group_clause:
! 			GROUP_P BY opt_gsets_quantifier gsets_element_list
! 				{
! 					GroupBy *n = makeNode(GroupBy);
! 					n->all = $3;
! 					n->grouping_sets = $4;
! 					n->location = @1;
! 					$$ = (Node *) n;
! 				}
! 			| /*EMPTY*/
! 				{
! 					$$ = NULL;
! 				}
! 		;
! 
! gsets_specification:
! 			GROUPING SETS '(' gsets_element_list ')'
! 				{
! 					GroupingSetsSpecification *gss = makeNode(GroupingSetsSpecification);
! 					gss->grouping_sets = $4;
! 					gss->has_empty_set = false;		/* not known now */
! 					gss->location = @1;
! 					$$ = (Node *) gss;
! 				}
! 
! gsets_element_list:
! 			gsets_element							{ $$ = list_make1($1); }
! 			| gsets_element_list ',' gsets_element			{ $$ = lappend($1, $3); }
! 		;
! 
! gsets_element:
! 			a_expr							{ $$ = $1; }
! 			| gsets_specification					{ $$ = $1; }
! 			| gsets_empty_set					{ $$ = $1; }
! 		;
! 
! gsets_empty_set:
! 			'(' ')'
! 				{
! 					$$ = (Node *) makeNode(GroupingSetsEmptySet);
! 				}
! 		;
! 
! opt_gsets_quantifier:
! 			ALL			{ $$ = true; }
! 			| DISTINCT		{ $$ = false; }
! 			| /*EMPTY*/		{ $$ = true; }
  		;
  
  having_clause:
***************
*** 9874,9879 ****
--- 9921,9968 ----
  					n->location = @1;
  					$$ = (Node *)n;
  				}
+ 			| GROUPING '(' a_expr ')'
+ 				{
+ 					GroupingSetsFunction *gsf = makeNode(GroupingSetsFunction);
+ 					gsf->kind = GROUPINGSETS_FUNCTION_GROUPING;
+ 					gsf->expr = $3;
+ 					gsf->expr_list = NIL;
+ 					gsf->location = @1;
+ 					$$ = (Node *) gsf;
+ 				}
+ 			| GROUPING_ID '(' expr_list ')'
+ 				{
+ 					GroupingSetsFunction *gsf = makeNode(GroupingSetsFunction);
+ 					gsf->kind = GROUPINGSETS_FUNCTION_GROUPING_ID;
+ 					gsf->expr = NULL;
+ 					gsf->expr_list = $3;
+ 					gsf->location = @1;
+ 					$$ = (Node *) gsf;
+ 				}
+ 			| CUBE '(' expr_list ')'
+ 				{
+ 					/*
+ 					 * Cube() and Rollup() are Grouping sets operators, but 
+ 					 * it has a same syntax like function call, and therefore
+ 					 * must be a processed here - other way means a parser's
+ 					 * conflict.
+ 					 */
+ 					GroupingSetsFunction *gsf = makeNode(GroupingSetsFunction);
+ 					gsf->kind = GROUPINGSETS_FUNCTION_CUBE;
+ 					gsf->expr = NULL;
+ 					gsf->expr_list = $3;
+ 					gsf->location = @1;
+ 					$$ = (Node *) gsf;
+ 				}
+ 			| ROLLUP '(' expr_list ')'
+ 				{
+ 					GroupingSetsFunction *gsf = makeNode(GroupingSetsFunction);
+ 					gsf->kind = GROUPINGSETS_FUNCTION_ROLLUP;
+ 					gsf->expr = NULL;
+ 					gsf->expr_list = $3;
+ 					gsf->location = @1;
+ 					$$ = (Node *) gsf;
+ 				}
  		;
  
  /*
***************
*** 11043,11048 ****
--- 11132,11138 ----
  			| SERVER
  			| SESSION
  			| SET
+ 			| SETS
  			| SHARE
  			| SHOW
  			| SIMPLE
***************
*** 11120,11125 ****
--- 11210,11216 ----
  			| EXTRACT
  			| FLOAT_P
  			| GREATEST
+ 			| GROUPING_ID
  			| INOUT
  			| INT_P
  			| INTEGER
***************
*** 11214,11219 ****
--- 11305,11311 ----
  			| COLUMN
  			| CONSTRAINT
  			| CREATE
+ 			| CUBE
  			| CURRENT_CATALOG
  			| CURRENT_DATE
  			| CURRENT_ROLE
***************
*** 11235,11240 ****
--- 11327,11333 ----
  			| FROM
  			| GRANT
  			| GROUP_P
+ 			| GROUPING
  			| HAVING
  			| IN_P
  			| INITIALLY
***************
*** 11256,11261 ****
--- 11349,11355 ----
  			| PRIMARY
  			| REFERENCES
  			| RETURNING
+ 			| ROLLUP
  			| SELECT
  			| SESSION_USER
  			| SOME
*** ./src/backend/parser/Makefile.orig	2010-08-09 15:34:35.458097731 +0200
--- ./src/backend/parser/Makefile	2010-08-09 15:35:01.924224125 +0200
***************
*** 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
  
*** ./src/backend/parser/parse_agg.c.orig	2010-08-09 15:34:35.459099259 +0200
--- ./src/backend/parser/parse_agg.c	2010-08-09 22:38:32.920097662 +0200
***************
*** 34,43 ****
--- 34,52 ----
  	int			sublevels_up;
  } check_ungrouped_columns_context;
  
+ typedef struct
+ {
+ 	ParseState *pstate;
+ 	List		*groupClause;
+ 	bool	isWhereClause;
+ } 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);
+ Node *transform_ungrouped_target(Node *node, ParseState *pstate, List *groupClauses, bool isWhereClause);
  
  
  /*
***************
*** 397,402 ****
--- 406,423 ----
  		}
  	}
  
+ 	/* check this state (for one set grouping sets), or parent state (for multiple grouping sets */
+ 	if (pstate->p_hasGroupingSets || (pstate->parentParseState && pstate->parentParseState->p_hasGroupingSets))
+ 	{
+ 		clause = (Node *) transform_ungrouped_target((Node *) qry->targetList, 
+ 										    pstate, 
+ 										    groupClauses, false);
+ 		/* HACK!!! - move to transform part*/
+ 		qry->targetList = (List *) clause;
+ 	}
+ 	else
+ 		clause = (Node *) qry->targetList;
+ 
  	/*
  	 * Check the targetlist and HAVING clause for ungrouped variables.
  	 *
***************
*** 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,
--- 426,431 ----
***************
*** 414,419 ****
--- 434,449 ----
  	clause = (Node *) qry->havingQual;
  	if (hasJoinRTEs)
  		clause = flatten_join_alias_vars(root, clause);
+ 		
+ 	if (pstate->p_hasGroupingSets || (pstate->parentParseState && pstate->parentParseState->p_hasGroupingSets))
+ 	{
+ 		
+ 		clause = (Node *) transform_ungrouped_target((Node *) clause,
+ 									    pstate,
+ 									    groupClauses, false);
+ 		qry->havingQual = clause;
+ 	}
+ 	
  	check_ungrouped_columns(clause, pstate,
  							groupClauses, have_non_var_grouping);
  
***************
*** 741,743 ****
--- 771,852 ----
  										 args,
  										 COERCE_DONTCARE);
  }
+ 
+ /*
+  * transform_ungrouped_cols_mutator -
+  *   All non aggregates, all non constatnt columns are replaced by NULL,
+  *   grouping and grouping_id functions are replaced by adequate 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, GroupingSetsFunction))
+ 	{
+ 		GroupingSetsFunction *gsf = (GroupingSetsFunction *) node;
+ 		int	result = 0;
+ 		
+ 		if (context->isWhereClause)
+ 			ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
+ 				        errmsg("Grouping sets function cannot be used in this clause"),
+ 				        parser_errposition(context->pstate, gsf->location)));
+ 		
+ 		if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING)
+ 		{
+ 			result = list_member(context->groupClause, gsf->expr) ? 0 : 1;
+ 			return (Node *) make_const(context->pstate, makeInteger(result), gsf->location);
+ 		}
+ 		else if (gsf->kind == GROUPINGSETS_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);
+ 		}
+ 	}
+ 	
+ 	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);
+ }
+ 
+ Node *
+ transform_ungrouped_target(Node *node, ParseState *pstate,
+ 							List *groupClauses, bool isWhereClause)
+ {
+ 	transform_ungrouped_target_context context;
+ 	
+ 	context.pstate = pstate;
+ 	context.groupClause = groupClauses;
+ 	context.isWhereClause = isWhereClause;
+ 	
+ 	return transform_ungrouped_target_mutator(node, &context);
+ }
*** ./src/backend/parser/parse_expr.c.orig	2010-08-09 15:34:35.461097776 +0200
--- ./src/backend/parser/parse_expr.c	2010-08-09 18:16:37.866097201 +0200
***************
*** 293,298 ****
--- 293,338 ----
  			result = transformCurrentOfExpr(pstate, (CurrentOfExpr *) expr);
  			break;
  
+ 		case T_GroupingSetsFunction:
+ 			{
+ 				GroupingSetsFunction *gsf = (GroupingSetsFunction *) expr;
+ 				
+ 				/*
+ 				 * Because there are parser's conflict between a_expr rules and CUBE() 
+ 				 * and ROLLUP() operator. But these operators cannot be used in expr.
+ 				 * So raise syntax error here.
+ 				 */
+ 				if (gsf->kind == GROUPINGSETS_FUNCTION_CUBE || gsf->kind == GROUPINGSETS_FUNCTION_ROLLUP)
+ 				{
+ 					ereport(ERROR, 
+ 							(errcode(ERRCODE_SYNTAX_ERROR),
+ 							 errmsg("grouping sets operator \"cube\" or \"rollup\" cannot be used here"),
+ 							 parser_errposition(pstate, gsf->location)));
+ 				}
+ 				else if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING)
+ 				{
+ 					/* this parser's node will be replaced later by NULL or constant */
+ 					gsf->expr = transformExpr(pstate, gsf->expr);
+ 					result = (Node *) gsf;
+ 				}
+ 				else if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING_ID)
+ 				{
+ 					List	   *expr_list = NIL;
+ 					ListCell	*lc;
+ 				
+ 					/* this parser's node will be replaced later by NULL or constant */
+ 					foreach(lc, gsf->expr_list)
+ 					{
+ 						expr_list = lappend(expr_list, 
+ 										transformExpr(pstate,
+ 													(Node *) lfirst(lc)));
+ 					}
+ 					gsf->expr_list = expr_list;
+ 					result = (Node *) gsf;
+ 				}
+ 			}
+ 			break;
+ 
  			/*********************************************
  			 * Quietly accept node types that may be presented when we are
  			 * called on an already-transformed tree.
***************
*** 324,329 ****
--- 364,370 ----
  		case T_CoerceToDomain:
  		case T_CoerceToDomainValue:
  		case T_SetToDefault:
+ 		case T_GroupingSetsEmptySet:
  			{
  				result = (Node *) expr;
  				break;
*** ./src/backend/parser/parse_gsets.c.orig	2010-08-09 15:34:48.079097706 +0200
--- ./src/backend/parser/parse_gsets.c	2010-08-09 15:35:01.927222352 +0200
***************
*** 0 ****
--- 1,637 ----
+ /*-------------------------------------------------------------------------
+  *
+  * parse_gsets.c
+  *		parser grouping sets transformations
+  *
+  * Portions Copyright (c) 1996-2010, 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_node.h"
+ #include "rewrite/rewriteManip.h"
+ 
+ #include <string.h>
+ 
+ #define MAX_CUBE_FIELDS		8
+ 
+ #define is_empty_list(n)		(n == NIL)
+ #define is_undef_loc(loc)		(loc == -1)
+ #define IsGroupingSetsSpecification(n)		(IsA(n, GroupingSetsSpecification))
+ 
+ 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 *evalGroupByClause(List *element_list, bool has_empty_set);
+ static Node *evalGroupingSetsOp(ParseState *pstate, Node *node);
+ 
+ /*
+  * return true, when GroupingSet is empty
+  */
+ static bool
+ is_empty_grouping_set(Node *node)
+ {
+ 	if (node == NULL)
+ 		return true;
+ 	if (IsA(node, GroupingSetsEmptySet))
+ 		return true;
+ 	if (IsGroupingSetsSpecification(node))
+ 		return is_empty_list(((GroupingSetsSpecification *) node)->grouping_sets);
+ 
+ 	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 *
+ evalGroupingSets(ParseState *pstate, List *gsets_element_list)
+ {
+ 	ListCell	*g;
+ 	List	*grouping_sets = NIL;
+ 	bool		has_empty_set = false;
+ 	int		location = -1;				/* undefined location now */
+ 	bool	use_grouping_sets = false;
+ 
+ 	/* leave fast when grouplist is NIL */
+ 	if (is_empty_list(gsets_element_list))
+ 		return NULL;
+ 
+ 	/* detect single group by grouping set */
+ 	foreach(g, gsets_element_list)
+ 	{
+ 		Node *el = (Node *) lfirst(g);
+ 		Node *evaluated_element;
+ 
+ 		/* 
+ 		 * Is it empty set. NULL is short cut for empty set. 
+ 		 * This has disadvantage - we lost location, but it is short.
+ 		 */
+ 		if (is_empty_grouping_set(el))
+ 		{
+ 			has_empty_set = true;
+ 			continue;
+ 		}
+ 		
+ 		/* take location from first item of list */
+ 		if (is_undef_loc(location))
+ 			location = exprLocation(el);
+ 
+ 		evaluated_element = evalGroupingSetsOp(pstate, el);
+ 		if (is_empty_grouping_set(evaluated_element))
+ 		{
+ 			has_empty_set = true;
+ 			continue;
+ 		}
+ 		
+ 		if (IsA(el, GroupingSetsSpecification))
+ 			use_grouping_sets = true;
+ 		else if (IsA(el, GroupingSetsFunction))
+ 		{
+ 			switch (((GroupingSetsFunction *) el)->kind)
+ 			{
+ 				case GROUPINGSETS_FUNCTION_CUBE:
+ 				case GROUPINGSETS_FUNCTION_ROLLUP:
+ 					use_grouping_sets = true;
+ 					break;
+ 				default:
+ 					break;		/* be compiler quiete */
+ 			}
+ 		}
+ 		
+ 		grouping_sets = lappend(grouping_sets, evaluated_element); 
+ 	}
+ 	
+ 	/* return original list of fields, when grouping sets are not used */
+ 	if (!use_grouping_sets && !has_empty_set)
+ 		return (Node *) gsets_element_list;
+ 	
+ 	/* multiple all sets in GROUP BY list */
+ 	pstate->p_hasGroupingSets = true;
+ 	
+ 	return (Node *) evalGroupByClause(grouping_sets, 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 
+  * because GROUPING SETS (a, (a,b)) is different from 
+  * GROUPING SETS (a, ROW(a,b))
+  */
+ static List **
+ prepareElements(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 (is_empty_grouping_set((Node *) lfirst(l)))
+ 		{
+ 			*has_empty_set = true;
+ 			continue;
+ 		}
+ 		
+ 		felements = list_append_unique(felements, lfirst(l));
+ 	}
+ 	
+ 	if (is_empty_list(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_RowExpr:
+ 				result[i++] = ((RowExpr *) el)->args;
+ 				break;
+ 			default:
+ 				result[i++] = list_make1(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
+  *      replace all GroupingSetsFunction nodes (inside GROUP BY clause) by 
+  *      GroupingSetsSpecification nodes.
+  *
+  * This routine hasn't effect on GroupingSetsFunction nodes used in target list. These
+  * nodes are transformed to function calls - together with evaluation of Grouping, and 
+  * Grouping_id functions under target list transformation.
+  */
+ static Node *
+ evalGroupingSetsOp(ParseState *pstate, Node *node)
+ {
+ 	bool		has_empty_set = false;
+ 	List	*grouping_sets = NIL;
+ 	GroupingSetsSpecification *result;
+ 	int		location = -1;			/* be a compiler quiete */
+ 
+ 	/* leave when node is actually empty set */
+ 	if (is_empty_grouping_set(node))
+ 		return NULL;
+ 	
+ 	switch (nodeTag(node))
+ 	{
+ 		case T_GroupingSetsFunction:
+ 			{
+ 				List	**elements_vect;
+ 			
+ 				GroupingSetsFunction *gsf = (GroupingSetsFunction *) node;
+ 				int	nfields = list_length(gsf->expr_list);
+ 				
+ 				if (gsf->kind == GROUPINGSETS_FUNCTION_CUBE && nfields > MAX_CUBE_FIELDS)
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ 							 errmsg("too much parameters for CUBE operator"),
+ 							 parser_errposition(pstate, gsf->location)));
+ 
+ 				/*
+ 				 * Because we will access to grouping set elements via index repeatly, then
+ 				 * store pointers to elements to vector. prepareElements does little bit more
+ 				 * it reduce duplicit elements and ensure so node type of every element will
+ 				 * be a List (native list of one field's list) - de facto GroupingSets is 
+ 				 * list of lists. Leave when all elements are empty sets.
+ 				 */
+ 				elements_vect = prepareElements(gsf->expr_list, &nfields, &has_empty_set);
+ 				if (elements_vect == NULL)
+ 					return NULL;
+ 				
+ 				if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING || gsf->kind == GROUPINGSETS_FUNCTION_GROUPING_ID)
+ 				{
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_SYNTAX_ERROR),
+ 							 errmsg("function \"grouping\" or \"grouping_id\" is used in wrong context"),
+ 							 parser_errposition(pstate, gsf->location)));
+ 				}
+ 				else if (gsf->kind == GROUPINGSETS_FUNCTION_CUBE)
+ 				{
+ 					/* 
+ 					 * Grouping sets CUBE operator evaluates parameters to set of parameter
+ 					 * combinations like CUBE(a,b) -> {{a,b},{a},{b},{}}. The simple way to
+ 					 * generate parameter combinations is using a bitmap shift:
+ 					 *     111 7 {a,b,c}, 110 6 {a,b}, 101 5 {a,c}, 100 4 {a}, 
+ 					 *       ... 001 1 c, 000 0 {}
+ 					 *
+ 					 * btab is array of initial values for n cycles iteration (based on 
+ 					 * number of unique grouping element)
+ 					 */
+ 					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);
+ 					List	  *current_set;
+ 					int		i;
+ 					
+ 					grouping_sets = NIL;
+ 					while (bitmap > 0)
+ 					{
+ 						unsigned int processed_bitmap = bitmap--;
+ 						
+ 						current_set = NIL;
+ 						for (i = 0; processed_bitmap > 0; i++)
+ 						{
+ 							if (processed_bitmap & selector)
+ 								current_set = list_union(current_set, elements_vect[i]);
+ 							processed_bitmap = (processed_bitmap << 1) & masc;
+ 						}
+ 						grouping_sets = lappend(grouping_sets, current_set);
+ 						
+ 						/* append a empty set */
+ 						has_empty_set = true;
+ 					}
+ 				}
+ 				else if (gsf->kind == GROUPINGSETS_FUNCTION_ROLLUP)
+ 				{
+ 					/*
+ 					 * Grouping Sets ROLLUP operator
+ 					 *  This operator evaluate set {a,b} to sets {{a,b},{a},{}}
+ 					 */
+ 					int	i;
+ 					int	j;
+ 					List *current_set;
+ 					 
+ 					grouping_sets = NIL;
+ 					for (i = 0; i < nfields; i++)
+ 					{
+ 						current_set = NIL;
+ 						for (j = 0; j < nfields - i; j++)
+ 							current_set = list_union(current_set, elements_vect[j]);
+ 						
+ 						grouping_sets = lappend(grouping_sets, current_set);
+ 					}
+ 					
+ 					/* append a empty set */
+ 					has_empty_set = true;
+ 				}
+ 			}
+ 			break;
+ 		case T_GroupingSetsSpecification:
+ 			{
+ 				/* 
+ 				 * Grouping Sets (xxxx)
+ 				 *  a) evaluates inner CUBE and ROLLUP operator
+ 				 *  b) drop duplicate sets
+ 				 *  c) detect inner empty sets
+ 				 */
+ 				ListCell	*gse;
+ 				GroupingSetsSpecification *gss = (GroupingSetsSpecification *) node;
+ 				
+ 				grouping_sets = NIL;
+ 				location = gss->location;
+ 				
+ 				foreach(gse, gss->grouping_sets)
+ 				{
+ 					Node *grouping_element = (Node *) lfirst(gse);
+ 					Node *evaluated_element = evalGroupingSetsOp(pstate, grouping_element);
+ 
+ 					if (is_empty_grouping_set(evaluated_element))
+ 					{
+ 						has_empty_set = true;
+ 						continue;
+ 					}
+ 					
+ 					if (IsGroupingSetsSpecification(evaluated_element))
+ 					{
+ 						GroupingSetsSpecification *egss = (GroupingSetsSpecification *) evaluated_element;
+ 						
+ 						if (egss->has_empty_set)
+ 							has_empty_set = true;
+ 						
+ 						if (egss->grouping_sets != NIL)
+ 							grouping_sets = add_distinct_sets(grouping_sets, egss->grouping_sets);
+ 					}
+ 					else if (IsA(evaluated_element, RowExpr))
+ 					{
+ 						/* 
+ 						 * There are not possible to parse inner grouping sets
+ 						 * directly (like grouping sets(a,b, >>(c,b)<< ) because
+ 						 * there are a conflict with implicit row parse rule. So
+ 						 * in this context we have to transform any RowExpr to
+ 						 * grouping set element.
+ 						 */
+ 						ListCell	*l;
+ 						RowExpr *row_expr = (RowExpr *) evaluated_element;
+ 						
+ 						foreach(l, row_expr->args)
+ 						{
+ 							if (is_empty_grouping_set((Node *) lfirst(l)))
+ 							{
+ 								has_empty_set = true;
+ 								break;
+ 							}
+ 						}
+ 						
+ 						grouping_sets = add_distinct_set(grouping_sets, row_expr->args);
+ 					}
+ 					else
+ 						grouping_sets = add_distinct_set(grouping_sets, list_make1(evaluated_element));
+ 				}
+ 			}
+ 			break;
+ 			
+ 		default:
+ 			return node;
+ 	}
+ 	
+ 	if (grouping_sets == NIL)
+ 		return NULL;
+ 
+ 	result = (GroupingSetsSpecification *) makeNode(GroupingSetsSpecification);
+ 	result->has_empty_set = has_empty_set;
+ 	result->grouping_sets = grouping_sets;
+ 	result->location = location;
+ 	
+ 	return (Node *) result;
+ }
+ 
+ /* multiple two Grouping Sets elements
+  */
+ static Node *
+ multiple_grouping_sets(Node *a, Node *b)
+ {
+ 	ListCell	*l;
+ 	ListCell	*lj;
+ 	List		*grouping_sets = NIL;
+ 	GroupingSetsSpecification	*result;
+ 	GroupingSetsSpecification	*gss_a;
+ 	GroupingSetsSpecification	*gss_b;
+ 
+ 	/* leave when nodes are equal */
+ 	if (equal(a, b))
+ 		return a;
+ 
+ 	if (!IsGroupingSetsSpecification(a) && !IsGroupingSetsSpecification(b))
+ 	{
+ 		/* 
+ 		 * when both items are elements, then create new set as union 
+ 		 * a x b = (a,b)
+ 		 */
+ 		GroupingSetsSpecification *gss = (GroupingSetsSpecification *) makeNode(GroupingSetsSpecification);
+ 		gss->grouping_sets = list_make1(list_make2(a,b));
+ 		gss->has_empty_set = false;
+ 		gss->location = exprLocation(a);
+ 
+ 		return (Node *) gss;
+ 	}
+ 	if (IsGroupingSetsSpecification(a) && !IsGroupingSetsSpecification(b))
+ 	{
+ 		Node *aux = a;
+ 		
+ 		/* when left operand is set and right not, then swap operands */
+ 		a = b; b = aux;
+ 	}
+ 	
+ 	if (!IsGroupingSetsSpecification(a) && IsGroupingSetsSpecification(b))
+ 	{
+ 		/* 
+ 		 * when left operand isn't set and right yes, copy a to every set in b 
+ 		 * a x (b,()) = ((a,b),(a))
+ 		 */
+ 		gss_b = (GroupingSetsSpecification *) b;
+ 		
+ 		foreach(l, gss_b->grouping_sets)
+ 		{
+ 			List *s = (List *) lfirst(l);
+ 
+ 			Assert(IsA(lfirst(l), List));
+ 
+ 			/* add item to all grouping sets */
+ 			s = list_append_unique(s, a);
+ 			grouping_sets = add_distinct_set(grouping_sets, s);
+ 		}
+ 
+ 		/* replace possible empty set in b by a */
+ 		if (gss_b->has_empty_set)
+ 		{
+ 			grouping_sets = add_distinct_set(grouping_sets, (list_make1(a)));
+ 			gss_b->has_empty_set = false;
+ 		}
+ 
+ 		gss_b->grouping_sets = grouping_sets;
+ 		
+ 		return (Node *) b;
+ 	}
+ 
+ 	/*
+ 	 * Both operand are grouping sets
+ 	 * ((A,B),(C)) * ((X,Y),()) = ((A,B,X,Y),(A,B),(C,X,Y),(C))
+          */
+ 	Assert(IsGroupingSetsSpecification(a) && IsGroupingSetsSpecification(b));
+ 	
+ 	gss_a = (GroupingSetsSpecification *) a;
+ 	gss_b = (GroupingSetsSpecification *) b;
+ 
+ 	result = (GroupingSetsSpecification *) makeNode(GroupingSetsSpecification);
+ 
+ 	foreach(l, gss_a->grouping_sets)
+ 	{
+ 		foreach(lj, gss_b->grouping_sets)
+ 			grouping_sets = add_distinct_set(grouping_sets, 
+ 					list_union((List *) lfirst(l),
+ 						   (List *) lfirst(lj)));
+ 		if  (gss_b->has_empty_set)
+ 			grouping_sets = add_distinct_set(grouping_sets, 
+ 						    (List *) lfirst(l));
+ 	}
+ 
+ 	if (gss_a->has_empty_set)
+ 		foreach(l, gss_b->grouping_sets)
+ 			grouping_sets = add_distinct_set(grouping_sets, (List *) lfirst(l)); 
+ 
+ 	result->grouping_sets = grouping_sets;
+ 	/* when both operands has a empty set, then result will have a empty set too */
+ 	result->has_empty_set = gss_a->has_empty_set && gss_b->has_empty_set;
+ 	result->location = gss_a->location;
+ 
+ 	return (Node *) result;
+ }
+ 
+ /*
+  * evaluate list of GROUP BY elements
+  *
+  * When any Grouping Sets is used, then multiply all parameters
+  * of GROUP BY A, B, C = ((A x B) x C)
+  */
+ static Node *
+ evalGroupByClause(List *element_list, bool has_empty_set)
+ {
+ 	int	nfields;
+ 	ListCell	*lc;
+ 	bool	initialized_stack = false;
+ 	Node	*stack = NULL;
+ 
+ 	/* fast leave when element_list is empty */
+ 	if (has_empty_set)
+ 	{
+ 		if (is_empty_list(element_list))
+ 		{
+ 			GroupingSetsSpecification *gss = makeNode(GroupingSetsSpecification);
+ 			gss->grouping_sets = NIL;
+ 			gss->has_empty_set = true;
+ 			gss->location = -1;
+ 			
+ 			return (Node *) gss;
+ 		}
+ 	}
+ 
+ 	Assert(element_list != NIL);
+ 	nfields = list_length(element_list);
+ 
+ 	foreach(lc, element_list)
+ 	{
+ 		Node *el = (Node *) lfirst(lc);
+ 
+ 		/* fill arithmetic stack */
+ 		if (!initialized_stack)
+ 		{
+ 			stack = el;
+ 			 
+ 			/*
+ 			 * a has_empty_set value goes from outside. Propagate it to stack, 
+ 			 * when it is true. When element isn't of GroupingSetsSpecification type,
+ 			 * then cannot to carry value of has_empty_set, and then we have to wrap
+ 			 * element to implicit GroupingSetsSpecification node.
+ 			 */
+ 			if (IsGroupingSetsSpecification(el))
+ 			{
+ 				if (has_empty_set)
+ 					((GroupingSetsSpecification *) stack)->has_empty_set = true;
+ 			}
+ 			else
+ 			{
+ 				if (has_empty_set)
+ 				{
+ 					GroupingSetsSpecification *gss = makeNode(GroupingSetsSpecification);
+ 					gss->grouping_sets = list_make1(list_make1(el));
+ 					gss->has_empty_set = true;
+ 					gss->location = exprLocation(el);
+ 					stack = (Node *) gss;
+ 				}
+ 			}
+ 
+ 			/* when there are only one parameter, leave function now */
+ 			if (nfields == 1)
+ 				return stack;
+ 
+ 			initialized_stack = true;
+ 			/* go to for next item */
+ 			continue;
+ 		}
+ 
+ 		Assert(stack != NULL);
+ 		stack = multiple_grouping_sets(stack, el);
+ 	}
+ 
+ 	return stack;
+ }
*** ./src/backend/parser/parse_target.c.orig	2010-08-09 15:34:35.482097728 +0200
--- ./src/backend/parser/parse_target.c	2010-08-09 17:10:04.418097472 +0200
***************
*** 1586,1591 ****
--- 1586,1604 ----
  		case T_XmlSerialize:
  			*name = "xmlserialize";
  			return 2;
+ 		case T_GroupingSetsFunction:
+ 			switch (((GroupingSetsFunction *) node)->kind)
+ 			{
+ 				case GROUPINGSETS_FUNCTION_GROUPING:
+ 					*name = "grouping";
+ 					break;
+ 				case GROUPINGSETS_FUNCTION_GROUPING_ID:
+ 					*name = "grouping_id";
+ 					break;
+ 				default:
+ 					break;		/* be a compiler quiet */
+ 			}
+ 			break;
  		default:
  			break;
  	}
*** ./src/include/nodes/nodes.h.orig	2010-08-09 15:34:35.483099256 +0200
--- ./src/include/nodes/nodes.h	2010-08-09 15:35:01.928222413 +0200
***************
*** 385,390 ****
--- 385,394 ----
  	T_XmlSerialize,
  	T_WithClause,
  	T_CommonTableExpr,
+ 	T_GroupingSetsFunction,
+ 	T_GroupingSetsSpecification,
+ 	T_GroupingSetsEmptySet,
+ 	T_GroupBy,
  
  	/*
  	 * TAGS FOR RANDOM OTHER STUFF
*** ./src/include/nodes/parsenodes.h.orig	2010-08-09 15:34:35.485097772 +0200
--- ./src/include/nodes/parsenodes.h	2010-08-09 15:35:01.929222823 +0200
***************
*** 378,383 ****
--- 378,435 ----
  } SortBy;
  
  /*
+  * GroupBy - for GROUP BY [ GROUPING SETS ] clause
+  */
+ typedef struct GroupBy
+ {
+ 	NodeTag		type;
+ 	bool		all;				/* value of grouping sets quantifier - default is true */
+ 	List	    *grouping_sets;			/* list of grouping sets */
+ 	int			location;		/* operator location, or -1 if none/unknown */
+ } GroupBy;
+ 
+ typedef enum GroupingSets_func_kind
+ {
+ 	GROUPINGSETS_FUNCTION_GROUPING,
+ 	GROUPINGSETS_FUNCTION_GROUPING_ID,
+ 	GROUPINGSETS_FUNCTION_CUBE,
+ 	GROUPINGSETS_FUNCTION_ROLLUP
+ } GroupingSets_func_kind;
+ 
+ /*
+  * GroupingSetsFunction - for functions Grouping, Grouping_id and grouping
+  * sets operators Cube, Rollup.
+  */
+ typedef struct GroupingSetsFunction
+ {
+ 	NodeTag		type;
+ 	GroupingSets_func_kind	kind;		/* identify kind of grouping sets function or operator */
+ 	Node	*expr;				/* a parameter for function Grouping */
+ 	List	   *expr_list;			/* parameters for Grouping_id and Cube and Rollup op */
+ 	int			location;	/* token location, or -1 if unknown */
+ } GroupingSetsFunction;
+ 
+ /*
+  * GroupingSetsSpecification - for GROUPING SETS clause
+  */
+ typedef struct GroupingSetsSpecification
+ {
+ 	NodeTag		type;
+ 	List	   *grouping_sets;		/* list of grouping sets */
+ 	bool		has_empty_set;		/* true, when grouping sets contains any empty set */
+ 	int			location;	/* token location, or -1 if unknown */
+ } GroupingSetsSpecification;
+ 
+ /*
+  * GroupingSetsEmptySet - for empty parentheses inside grouping sets specification
+  */
+ typedef struct GroupingSetsEmptySet
+ {
+ 	NodeTag		type;
+ 	int			location;	/* token location, or -1 if unknown */
+ } GroupingSetsEmptySet;
+ 
+ /*
   * WindowDef - raw representation of WINDOW and OVER clauses
   *
   * For entries in a WINDOW list, "name" is the window name being defined.
***************
*** 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 */
--- 1008,1014 ----
  	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 */
*** ./src/include/parser/kwlist.h.orig	2010-08-09 15:34:35.486099719 +0200
--- ./src/include/parser/kwlist.h	2010-08-09 15:35:01.930224909 +0200
***************
*** 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)
***************
*** 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)
***************
*** 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)
***************
*** 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)
*** ./src/include/parser/parse_node.h.orig	2010-08-09 15:34:35.487097545 +0200
--- ./src/include/parser/parse_node.h	2010-08-09 15:35:01.931222805 +0200
***************
*** 108,113 ****
--- 108,114 ----
  	bool		p_locked_from_parent;
  	Relation	p_target_relation;
  	RangeTblEntry *p_target_rangetblentry;
+ 	bool		p_hasGroupingSets;
  
  	/*
  	 * Optional hook functions for parser callbacks.  These are null unless
***************
*** 149,152 ****
--- 150,155 ----
  						 Node *assignFrom);
  extern Const *make_const(ParseState *pstate, Value *value, int location);
  
+ extern Node *evalGroupingSets(ParseState *pstate, List *gsets_element_list);
+ 
  #endif   /* PARSE_NODE_H */
#2Joshua Tolley
eggyknap@gmail.com
In reply to: Pavel Stehule (#1)
Re: grouping sets - updated patch

On Mon, Aug 09, 2010 at 10:59:26PM +0200, Pavel Stehule wrote:

Hello

I fixed an issues with empty sets. It just work, but there are some ugly hacks.

It's really needs own planner node - now grouping functions are not
supported by ORDER BY clause.

I haven't made it through the last version much, but I'll poke through this
instead. I have a few days of family business coming up, and might be
unrespondive during that time.

--
Joshua Tolley / eggyknap
End Point Corporation
http://www.endpoint.com

#3Pavel Stehule
pavel.stehule@gmail.com
In reply to: Joshua Tolley (#2)
Re: grouping sets - updated patch

2010/8/10 Joshua Tolley <eggyknap@gmail.com>:

On Mon, Aug 09, 2010 at 10:59:26PM +0200, Pavel Stehule wrote:

Hello

I fixed an issues with empty sets. It just work, but there are some ugly hacks.

It's really needs own planner node - now grouping functions are not
supported by ORDER BY clause.

I haven't made it through the last version much, but I'll poke through this
instead. I have a few days of family business coming up, and might be
unrespondive during that time.

ok,

Pavel

Show quoted text

--
Joshua Tolley / eggyknap
End Point Corporation
http://www.endpoint.com

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)

iEYEARECAAYFAkxgxlgACgkQRiRfCGf1UMM3NwCgkJ3EEWIj6MLiDcU2SHT/hH7a
4BsAn2hTqqzsLYLFZbflIJK/x/WMsZ2d
=dIod
-----END PGP SIGNATURE-----