GROUPING SETS revisited

Started by Joshua Tolleyover 15 years ago17 messages
#1Joshua Tolley
eggyknap@gmail.com
1 attachment(s)

In case anyone's interested, I've taken the CTE-based grouping sets patch from
[1]: http://archives.postgresql.org/pgsql-hackers/2009-05/msg00700.php
it for whitespace consistency, style conformity, or anything else, but (tuits
permitting) hope to figure out how it works and get it closer to commitability
in some upcoming commitfest.

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

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

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

Attachments:

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

Hello

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

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

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

I am plaing with it now :). I have a plan to replace CTE with similar
but explicit executor node. The main issue of existing patch was using
just transformation to CTE. I agree, so it isn't too much extensiable
in future. Now I am cleaning identifiers little bit. Any colaboration
is welcome.

My plan:
1) clean CTE patch
2) replace CTE with explicit executor node, but still based on tuplestore
3) when will be possible parallel processing based on hash agg - then
we don't need to use tuplestore

comments??

Regards

Pavel

Show quoted text

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

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

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

iEYEARECAAYFAkxXrggACgkQRiRfCGf1UMMlCQCglaIdtPj8Qe6G60V2LHn5pFNn
kgIAniXRgIQEbVrK/eDVZnmKCzw33lT9
=XVVV
-----END PGP SIGNATURE-----

#3Hitoshi Harada
umi.tanuki@gmail.com
In reply to: Pavel Stehule (#2)
Re: GROUPING SETS revisited

2010/8/3 Pavel Stehule <pavel.stehule@gmail.com>:

Hello

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

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

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

I am plaing with it now :). I have a plan to replace CTE with similar
but explicit executor node. The main issue of existing patch was using
just transformation to CTE. I agree, so it isn't too much extensiable
in future. Now I am cleaning identifiers little bit. Any colaboration
is welcome.

My plan:
1) clean CTE patch
2) replace CTE with explicit executor node, but still based on tuplestore
3) when will be possible parallel processing based on hash agg - then
we don't need to use tuplestore

Couldn't you explain what exactly "explicit executor node"? I hope we
can share your image to develop it further than only transformation to
CTE.

Regards,

--
Hitoshi Harada

#4Pavel Stehule
pavel.stehule@gmail.com
In reply to: Hitoshi Harada (#3)
Re: GROUPING SETS revisited

2010/8/3 Hitoshi Harada <umi.tanuki@gmail.com>:

2010/8/3 Pavel Stehule <pavel.stehule@gmail.com>:

Hello

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

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

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

I am plaing with it now :). I have a plan to replace CTE with similar
but explicit executor node. The main issue of existing patch was using
just transformation to CTE. I agree, so it isn't too much extensiable
in future. Now I am cleaning identifiers little bit. Any colaboration
is welcome.

My plan:
1) clean CTE patch
2) replace CTE with explicit executor node, but still based on tuplestore
3) when will be possible parallel processing based on hash agg - then
we don't need to use tuplestore

Couldn't you explain what exactly "explicit executor node"? I hope we
can share your image to develop it further than only transformation to
CTE.

I have a one reason

Implementation based on CTE doesn't create space for possible
optimalisations (I think now, maybe it isn't true). It is good for
initial or referencial implementation - but it can be too complex,
when we will try to append some optimalizations - like parallel hash
agg processing, direct data reading without tuplestore. If you are, as
CTE author, thinking so these features are possible in non recursive
CTE too, I am not agains. I hope so this week I'll have a CTE based
patch - and we can talk about next direction. I see as possible
performance issue using a tuplestore - there are lot of cases where
repeating of source query can be faster.

If I remember well, Tom has a objection, so transformation to CTE is
too early - in parser. So It will be first change. Executor node can
be CTE.

regards

Pavel

p.s. I am sure, so there are lot of task, that can be solved together
with non recursive CTE.

Show quoted text

Regards,

--
Hitoshi Harada

#5David Fetter
david@fetter.org
In reply to: Joshua Tolley (#1)
2 attachment(s)
Re: GROUPING SETS revisited

On Mon, Aug 02, 2010 at 11:50:00PM -0600, Josh Tolley wrote:

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

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

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

I've added back one file in the patch enclosed here. I'm still
getting compile fails from

CC="ccache gcc" ./configure --prefix=$PG_PREFIX --with-pgport=$PGPORT --with-perl --with-libxml --enable-debug --enable-cassert
make

Log from that also enclosed.

Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics

Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate

Attachments:

gs91_02.patchtext/plain; charset=us-asciiDownload
diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile
index a8f4c07..fb248a6 100644
--- a/src/backend/parser/Makefile
+++ b/src/backend/parser/Makefile
@@ -15,7 +15,7 @@ override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
 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_target.o parse_type.o parse_utilcmd.o scansup.o parse_gsets.o
 
 FLEXFLAGS = -CF
 
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 6b99a10..1b579a8 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -34,6 +34,7 @@
 #include "parser/parse_clause.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_cte.h"
+#include "parser/parse_gsets.h"
 #include "parser/parse_oper.h"
 #include "parser/parse_param.h"
 #include "parser/parse_relation.h"
@@ -150,6 +151,163 @@ parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
 }
 
 /*
+ * process GROUPING SETS
+ */
+static SelectStmt *
+makeSelectStmt(List *targetList, List *fromClause)
+{
+	SelectStmt *n = makeNode(SelectStmt);
+	n->distinctClause = NULL;
+	n->intoClause = NULL;
+	n->targetList = targetList;
+	n->fromClause = fromClause;
+	n->whereClause = NULL;
+	n->groupClause = NULL;
+	n->havingClause = NULL;
+	n->windowClause = NIL;
+	n->withClause = NULL;
+	n->valuesLists = NIL;
+	n->sortClause = NIL;
+	n->limitOffset = NULL;
+	n->limitCount = NULL;
+	n->lockingClause = NIL;
+	n->op = SETOP_NONE;
+	n->all = false;
+	n->larg = NULL;
+	n->rarg = NULL;
+	return n;
+}
+
+static List *
+makeStarTargetList(void)
+{
+	ResTarget *rt = makeNode(ResTarget);
+	
+	rt->name = NULL;
+	rt->indirection = NIL;
+	rt->val = (Node *) makeNode(ColumnRef);
+	((ColumnRef *) rt->val)->fields = list_make1(makeNode(A_Star));
+	rt->location = -1;
+
+	return list_make1(rt);
+}
+ 
+static SelectStmt *
+transformGroupingSets(ParseState *pstate, SelectStmt *stmt)
+{
+	if (stmt->groupClause && IsA(stmt->groupClause, GroupByClause))
+	{
+		GroupingSetsSpec *gss = (GroupingSetsSpec *) expandGroupingSets(pstate, 
+						(List *)((GroupByClause *)stmt->groupClause)->fields);
+	
+		if (pstate->p_hasGroupingSets)
+		{
+			CommonTableExpr *cte = makeNode(CommonTableExpr);
+			SelectStmt  *cteedstmt;
+			int	ngroupingsets = list_length(gss->set_list) + (gss->has_empty_set ? 1 : 0);
+			bool	all = ((GroupByClause *) stmt->groupClause)->all;
+		
+			cteedstmt = makeSelectStmt(NIL, NIL);
+			cteedstmt->intoClause = stmt->intoClause;
+			cteedstmt->sortClause = stmt->sortClause;
+			cteedstmt->limitOffset = stmt->limitOffset;
+			cteedstmt->limitCount = stmt->limitCount;
+			cteedstmt->lockingClause = stmt->lockingClause;
+		
+			cte->ctename = "**g**";
+			cte->ctequery = (Node *) stmt;
+			cte->location = -1;
+		
+			cteedstmt->withClause = makeNode(WithClause);
+			cteedstmt->withClause->ctes = list_make1(cte);
+			cteedstmt->withClause->recursive = false;
+			cteedstmt->withClause->location = -1;
+		
+			/* when is more than one grouping set, then we should generate setop node */
+			if (ngroupingsets > 1)
+			{
+				/* add quuery under union all for every grouping set */
+				SelectStmt	*larg = NULL;
+				SelectStmt	*rarg;
+				ListCell    *lc;
+			
+				foreach(lc, gss->set_list)
+				{
+					List	*groupClause;
+				
+					Assert(IsA(lfirst(lc), List));
+					groupClause = (List *) lfirst(lc);
+				
+					if (larg == NULL)
+					{
+						larg = makeSelectStmt(copyObject(stmt->targetList),
+									list_make1(makeRangeVar(NULL, "**g**", -1)));
+						larg->groupClause = (Node *) groupClause;
+						larg->havingClause = copyObject(stmt->havingClause);
+					}
+					else
+					{
+						SelectStmt	*setop = makeSelectStmt(NIL, NIL);
+					
+						rarg = makeSelectStmt(copyObject(stmt->targetList),
+									list_make1(makeRangeVar(NULL, "**g**", -1)));
+						rarg->groupClause = (Node *) groupClause;
+						rarg->havingClause = copyObject(stmt->havingClause);
+					
+						setop->op = SETOP_UNION;
+						setop->larg = larg;
+						setop->rarg = rarg;
+						setop->all = all;
+					
+						larg = setop;
+					}
+				}
+				if (gss->has_empty_set)
+				{
+					SelectStmt	*setop = makeSelectStmt(NIL, NIL);
+				
+					rarg = makeSelectStmt(copyObject(stmt->targetList),
+								list_make1(makeRangeVar(NULL, "**g**", -1)));
+					rarg->havingClause = copyObject(stmt->havingClause);
+				
+					setop->op = SETOP_UNION;
+					setop->larg = larg;
+					setop->rarg = rarg;
+					setop->all = all;
+					
+					larg = setop;
+				}
+				/* merge larg to result */
+				cteedstmt->op = larg->op;
+				cteedstmt->larg = larg->larg;
+				cteedstmt->rarg = larg->rarg;
+				cteedstmt->all = larg->all;
+			}
+			else
+			{
+				/* there isn't used setop node */
+				cteedstmt->targetList = copyObject(stmt->targetList);
+				cteedstmt->fromClause = list_make1(makeRangeVar(NULL, "**g**", -1));
+			}
+		
+			((SelectStmt *)cte->ctequery)->targetList = makeStarTargetList();
+			((SelectStmt *)cte->ctequery)->groupClause = NULL;
+			((SelectStmt *)cte->ctequery)->sortClause = NIL;
+			((SelectStmt *)cte->ctequery)->limitOffset = stmt->limitOffset;
+			((SelectStmt *)cte->ctequery)->limitCount = stmt->limitCount;
+			((SelectStmt *)cte->ctequery)->lockingClause = stmt->lockingClause;
+		
+			return cteedstmt;
+		}
+		else
+			/* trim GroupByClause to groupByClause */
+			stmt->groupClause = (Node *)((GroupByClause *)stmt->groupClause)->fields;
+	}
+
+	return stmt;
+}
+
+/*
  * transformStmt -
  *	  transform a Parse tree into a Query tree.
  */
@@ -179,6 +337,8 @@ transformStmt(ParseState *pstate, Node *parseTree)
 			{
 				SelectStmt *n = (SelectStmt *) parseTree;
 
+  				n = transformGroupingSets(pstate, n);
+
 				if (n->valuesLists)
 					result = transformValuesClause(pstate, n);
 				else if (n->op == SETOP_NONE)
@@ -827,7 +987,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 										  false /* allow SQL92 rules */ );
 
 	qry->groupClause = transformGroupClause(pstate,
-											stmt->groupClause,
+											(List *) stmt->groupClause,
 											&qry->targetList,
 											qry->sortClause,
 											false /* allow SQL92 rules */ );
@@ -924,7 +1084,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
 	Assert(stmt->targetList == NIL);
 	Assert(stmt->fromClause == NIL);
 	Assert(stmt->whereClause == NULL);
-	Assert(stmt->groupClause == NIL);
+	Assert(stmt->groupClause == NULL);
 	Assert(stmt->havingClause == NULL);
 	Assert(stmt->windowClause == NIL);
 	Assert(stmt->op == SETOP_NONE);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1722036..0c9eff1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -111,6 +111,8 @@ static Node *makeBitStringConst(char *str, int location);
 static Node *makeNullAConst(int location);
 static Node *makeAConst(Value *v, int location);
 static Node *makeBoolAConst(bool state, int location);
+static Node *makeGroupingSetsFunc(GroupingSetsFuncIdentity identity, Node *expr, 
+							List *expr_list, int location);
 static FuncCall *makeOverlaps(List *largs, List *rargs,
 							  int location, core_yyscan_t yyscanner);
 static void check_qualified_name(List *names, core_yyscan_t yyscanner);
@@ -292,7 +294,7 @@ static TypeName *TableFuncTypeName(List *columns);
 				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
+  				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,6 +439,11 @@ static TypeName *TableFuncTypeName(List *columns);
 				opt_frame_clause frame_extent frame_bound
 %type <str>		opt_existing_window_name
 
+%type <node>	grouping_element empty_grouping_set grouping_sets_spec
+				group_clause
+%type <list>	grouping_element_list
+%type <boolean>	opt_grouping_set_quantifier
+  
 
 /*
  * Non-keyword token types.  These are hard-wired into the "flex" lexer.
@@ -472,7 +479,7 @@ static TypeName *TableFuncTypeName(List *columns);
 	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
+	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
@@ -485,7 +492,7 @@ static TypeName *TableFuncTypeName(List *columns);
 	FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
 	FREEZE FROM FULL FUNCTION FUNCTIONS
 
-	GLOBAL GRANT GRANTED GREATEST GROUP_P
+	GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING GROUPING_ID
 
 	HANDLER HAVING HEADER_P HOLD HOUR_P
 
@@ -519,10 +526,10 @@ static TypeName *TableFuncTypeName(List *columns);
 
 	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
+	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 SHARE
+	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
@@ -7797,9 +7804,55 @@ first_or_next: FIRST_P								{ $$ = 0; }
 
 
 group_clause:
-			GROUP_P BY expr_list					{ $$ = $3; }
-			| /*EMPTY*/								{ $$ = NIL; }
-		;
+  			GROUP_P BY opt_grouping_set_quantifier grouping_element_list		
+  					{ 
+  						GroupByClause *clause = makeNode(GroupByClause);
+  						clause->all = $3;
+  						clause->fields = $4;
+  						clause->location = @1;
+  						$$ = (Node *) clause; 
+  					}
+  			| /*EMPTY*/
+  					{
+  						$$ = NULL; 
+  					}
+  		;
+  
+grouping_sets_spec:
+  			GROUPING SETS '(' grouping_element_list ')'
+  				{
+  					/* We cannot identify and drop empty sets yet. */
+  					GroupingSetsSpec *gss = makeNode(GroupingSetsSpec);
+  					gss->set_list = $4;
+  					gss->has_empty_set = false;
+  					gss->location = @1;
+  					$$ = (Node *) gss;
+  				}
+  		;
+  		
+grouping_element_list:
+  			grouping_element							{ $$ = list_make1($1); }
+  			| grouping_element_list ',' grouping_element			{ $$ = lappend($1, $3); }
+  		;
+  		
+grouping_element:
+  			a_expr							{ $$ = $1; }
+  			| grouping_sets_spec					{ $$ = $1; }
+  			| empty_grouping_set					{ $$ = $1; }
+  		;
+  
+empty_grouping_set:
+  			'(' ')'
+  				{
+  					$$ = makeNode(EmptySet);
+  				}
+  		;
+  
+opt_grouping_set_quantifier:
+  			ALL			{ $$ = true; }
+  			| DISTINCT		{ $$ = false; }
+  			| /*EMPTY*/		{ $$ = true; }
+  		;
 
 having_clause:
 			HAVING a_expr							{ $$ = $2; }
@@ -9326,7 +9379,30 @@ c_expr:		columnref								{ $$ = $1; }
 					r->location = @1;
 					$$ = (Node *)r;
 				}
-		;
+  			| GROUPING '(' a_expr ')'
+  				{
+  					$$ = makeGroupingSetsFunc(FUNCTION_GROUPING, $3, NIL, @1);
+  				}
+  			| GROUPING_ID '(' expr_list ')'
+  				{
+  					$$ = makeGroupingSetsFunc(FUNCTION_GROUPING_ID, NULL, $3, @1);
+  				}
+  			| CUBE '(' expr_list ')'
+  				{
+  					/* 
+  					 * Cube should be used in two different contexts. First,
+  					 * as part of Grouping Sets specification. Second, as
+  					 * normal UDF function from contrib cube module. When isnot 
+  					 * grouping sets context, then node is transformated to 
+  					 * FuncCall node later.
+  					 */
+  					 $$ = makeGroupingSetsFunc(FUNCTION_CUBE, NULL, $3, @1);
+  				}
+  			| ROLLUP '(' expr_list ')'
+  				{
+  					$$ = makeGroupingSetsFunc(FUNCTION_ROLLUP, NULL, $3, @1);
+  				}
+  		;
 
 /*
  * func_expr is split out from c_expr just so that we have a classification
@@ -11043,6 +11119,7 @@ unreserved_keyword:
 			| SERVER
 			| SESSION
 			| SET
+			| SETS
 			| SHARE
 			| SHOW
 			| SIMPLE
@@ -11120,6 +11197,7 @@ col_name_keyword:
 			| EXTRACT
 			| FLOAT_P
 			| GREATEST
+            | GROUPING_ID
 			| INOUT
 			| INT_P
 			| INTEGER
@@ -11214,6 +11292,7 @@ reserved_keyword:
 			| COLUMN
 			| CONSTRAINT
 			| CREATE
+            | CUBE
 			| CURRENT_CATALOG
 			| CURRENT_DATE
 			| CURRENT_ROLE
@@ -11235,6 +11314,7 @@ reserved_keyword:
 			| FROM
 			| GRANT
 			| GROUP_P
+            | GROUPING
 			| HAVING
 			| IN_P
 			| INITIALLY
@@ -11256,6 +11336,7 @@ reserved_keyword:
 			| PRIMARY
 			| REFERENCES
 			| RETURNING
+            | ROLLUP
 			| SELECT
 			| SESSION_USER
 			| SOME
@@ -11761,6 +11842,18 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
 	return (Node *) x;
 }
 
+static Node *
+makeGroupingSetsFunc(GroupingSetsFuncIdentity identity, Node *expr, List *expr_list, int location)
+{
+	GroupingSetsFunc *gsfunc = makeNode(GroupingSetsFunc);
+	
+	gsfunc->identity = identity;
+	gsfunc->expr = expr;
+	gsfunc->expr_list = expr_list;
+	gsfunc->location = location;
+	return (Node *) gsfunc;
+}
+
 /* parser_init()
  * Initialize to parse one query string
  */
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 0a69bde..3aeea45 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -33,12 +33,20 @@ typedef struct
 	bool		have_non_var_grouping;
 	int			sublevels_up;
 } check_ungrouped_columns_context;
+  
+typedef struct
+{
+	ParseState *pstate;
+	List		*groupClause;
+} transform_ungrouped_target_context;
 
 static void check_ungrouped_columns(Node *node, ParseState *pstate,
 						List *groupClauses, bool have_non_var_grouping);
 static bool check_ungrouped_columns_walker(Node *node,
 							   check_ungrouped_columns_context *context);
 
+static Node * transform_ungrouped_target(Node *node, ParseState *pstate,
+							List *groupClauses);
 
 /*
  * transformAggregateCall -
@@ -405,7 +413,16 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
 	 * 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 (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,
@@ -514,6 +531,83 @@ parseCheckWindowFuncs(ParseState *pstate, Query *qry)
 }
 
 /*
+ * transform_ungrouped_cols_mutator -
+ *   All non aggregates non constatnt columns are replaced by NULL,
+ *   grouping and grouping_id functions are replaced by constatnts.
+ */
+static Node *
+transform_ungrouped_target_mutator(Node *node,
+					     transform_ungrouped_target_context *context)
+{
+	if (node == NULL)
+		return NULL;
+	if (IsA(node, Aggref))
+		return node;
+
+	if (IsA(node, GroupingSetsFunc))
+	{
+		GroupingSetsFunc *gsf = (GroupingSetsFunc *) node;
+		int	result = 0;
+		
+		if (gsf->identity == FUNCTION_GROUPING)
+		{
+			result = list_member(context->groupClause, gsf->expr) ? 0 : 1;
+			return (Node *) make_const(context->pstate, makeInteger(result), gsf->location);
+		}
+		else if (gsf->identity == FUNCTION_GROUPING_ID)
+		{
+			ListCell	*el;
+			
+			foreach(el, gsf->expr_list)
+			{
+				result = result << 1;
+				if (!list_member(context->groupClause, lfirst(el)))
+					result = result | 0x01;
+			}
+			return (Node *) make_const(context->pstate, makeInteger(result), gsf->location);
+		}
+		else /* replace Cube and Rollup by FuncCall node */
+		{
+			/* ToDo: Support cube function */
+		}
+	}
+	
+	if (IsA(node, Var))
+	{
+		Var *var = (Var *) node;
+
+		if (list_member(context->groupClause, node))
+			return node;
+		else
+			return (Node *) makeNullConst(var->vartype, var->vartypmod);
+	}
+	else if (IsA(node, FuncExpr))
+	{
+		FuncExpr *fexpr = (FuncExpr *) node;
+		
+		if (list_member(context->groupClause, node))
+			return node;
+		else
+			return (Node *) makeNullConst(fexpr->funcresulttype, -1);
+	}
+	
+	return expression_tree_mutator(node, 
+					    transform_ungrouped_target_mutator, context);
+}
+
+static Node *
+transform_ungrouped_target(Node *node, ParseState *pstate,
+							List *groupClauses)
+{
+	transform_ungrouped_target_context context;
+	
+	context.pstate = pstate;
+	context.groupClause = groupClauses;
+	
+	return transform_ungrouped_target_mutator(node, &context);
+}
+
+/*
  * check_ungrouped_columns -
  *	  Scan the given expression tree for ungrouped variables (variables
  *	  that are not listed in the groupClauses list and are not within
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 5e60374..609be1d 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -284,6 +284,25 @@ transformExpr(ParseState *pstate, Node *expr)
 		case T_BooleanTest:
 			result = transformBooleanTest(pstate, (BooleanTest *) expr);
 			break;
+ 		
+ 		case T_GroupingSetsFunc:
+ 			{
+ 				GroupingSetsFunc *gsf = (GroupingSetsFunc *) expr;
+ 				ListCell	*lc;
+ 				List		*expr_list = NIL;
+ 				
+ 				gsf->expr = (Node *) transformExpr(pstate, (Node *) gsf->expr);
+ 
+ 				foreach(lc, gsf->expr_list)
+ 				{
+ 					expr_list = lappend(expr_list, transformExpr(pstate,
+ 											 (Node *) lfirst(lc)));
+ 				}
+ 				gsf->expr_list = expr_list;
+ 				result = expr;
+ 				break;
+ 			}
+  
 
 		case T_CurrentOfExpr:
 			result = transformCurrentOfExpr(pstate, (CurrentOfExpr *) expr);
@@ -320,6 +339,7 @@ transformExpr(ParseState *pstate, Node *expr)
 		case T_CoerceToDomain:
 		case T_CoerceToDomainValue:
 		case T_SetToDefault:
+        case T_EmptySet:
 			{
 				result = (Node *) expr;
 				break;
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index e542dc0..1a3e969 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1586,6 +1586,22 @@ FigureColnameInternal(Node *node, char **name)
 		case T_XmlSerialize:
 			*name = "xmlserialize";
 			return 2;
+ 		case T_GroupingSetsFunc:
+ 			switch (((GroupingSetsFunc *) node)->identity)
+ 			{
+ 				case FUNCTION_GROUPING:
+ 					*name = "grouping";
+ 					return 2;
+ 				case FUNCTION_GROUPING_ID:
+ 					*name = "grouping_id";
+ 					return 2;
+ 				case FUNCTION_CUBE:		/* by compiler quite */
+ 				case FUNCTION_ROLLUP:
+ 					/* nothing */
+ 					break;
+ 			}
+ 			break;
+ 			
 		default:
 			break;
 	}
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index a5f5df5..836e38d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -385,6 +385,10 @@ typedef enum NodeTag
 	T_XmlSerialize,
 	T_WithClause,
 	T_CommonTableExpr,
+	T_GroupingSetsFunc,
+	T_GroupingSetsSpec,
+	T_EmptySet,
+	T_GroupByClause,
 
 	/*
 	 * TAGS FOR RANDOM OTHER STUFF
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index fec8d3c..904cb4a 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -378,6 +378,33 @@ typedef struct SortBy
 } SortBy;
 
 /*
+ * GroupingSetsSpec - for GROUP BY GROUPING SETS clause
+ */
+typedef struct GroupingSetsSpec
+{
+	NodeTag		type;
+	List	    *set_list;
+	bool		has_empty_set; /* true when grouping sets contains empty set */
+	int			location;
+} GroupingSetsSpec;
+
+typedef struct EmptySet
+{
+	NodeTag		type;
+} EmptySet;
+
+/*
+ * GroupByClause for GROUP BY clause
+ */
+typedef struct GroupByClause
+{
+	NodeTag		type;
+	bool	all;
+	List		*fields;
+	int			location;
+} GroupByClause;
+
+/*
  * WindowDef - raw representation of WINDOW and OVER clauses
  *
  * For entries in a WINDOW list, "name" is the window name being defined.
@@ -431,6 +458,26 @@ typedef struct WindowDef
 	 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
@@ -956,7 +1003,7 @@ typedef struct SelectStmt
 	List	   *targetList;		/* the target list (of ResTarget) */
 	List	   *fromClause;		/* the FROM clause */
 	Node	   *whereClause;	/* WHERE qualification */
-	List	   *groupClause;	/* GROUP BY clauses */
+	Node	   *groupClause;	/* GROUP BY clauses */
 	Node	   *havingClause;	/* HAVING conditional-expression */
 	List	   *windowClause;	/* WINDOW window_name AS (...), ... */
 	WithClause *withClause;		/* WITH clause */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 49d4b6c..d0dcfe7 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -99,6 +99,7 @@ PG_KEYWORD("createrole", CREATEROLE, UNRESERVED_KEYWORD)
 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,6 +172,8 @@ PG_KEYWORD("grant", GRANT, RESERVED_KEYWORD)
 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,6 +321,7 @@ PG_KEYWORD("revoke", REVOKE, UNRESERVED_KEYWORD)
 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,6 +340,7 @@ PG_KEYWORD("session", SESSION, UNRESERVED_KEYWORD)
 PG_KEYWORD("session_user", SESSION_USER, RESERVED_KEYWORD)
 PG_KEYWORD("set", SET, UNRESERVED_KEYWORD)
 PG_KEYWORD("setof", SETOF, COL_NAME_KEYWORD)
+PG_KEYWORD("sets", SETS, UNRESERVED_KEYWORD)
 PG_KEYWORD("share", SHARE, UNRESERVED_KEYWORD)
 PG_KEYWORD("show", SHOW, UNRESERVED_KEYWORD)
 PG_KEYWORD("similar", SIMILAR, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_gsets.h b/src/include/parser/parse_gsets.h
new file mode 100644
index 0000000..98a9161
--- /dev/null
+++ b/src/include/parser/parse_gsets.h
@@ -0,0 +1,12 @@
+#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 */
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 1c1383b..f2b80bd 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -107,6 +107,7 @@ struct ParseState
 	bool		p_is_update;
 	bool		p_locked_from_parent;
 	Relation	p_target_relation;
+    bool        p_hasGroupingSets;
 	RangeTblEntry *p_target_rangetblentry;
 
 	/*
make.logtext/plain; charset=utf-8Download
#6Joshua Tolley
eggyknap@gmail.com
In reply to: David Fetter (#5)
Re: GROUPING SETS revisited

On Tue, Aug 03, 2010 at 12:58:03PM -0700, David Fetter wrote:

On Mon, Aug 02, 2010 at 11:50:00PM -0600, Josh Tolley wrote:

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

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

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

I've added back one file in the patch enclosed here. I'm still
getting compile fails from

CC="ccache gcc" ./configure --prefix=$PG_PREFIX --with-pgport=$PGPORT --with-perl --with-libxml --enable-debug --enable-cassert
make

Log from that also enclosed.

Yeah, I seem to have done a poor job of producing the patch based on the
repository I was working from. That said, it seems Pavel's working actively on
a patch anyway, so perhaps my updating the old one isn't all that worthwhile.
Pavel, is your code somewhere that we can get to it?

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

#7Pavel Stehule
pavel.stehule@gmail.com
In reply to: Joshua Tolley (#6)
Re: GROUPING SETS revisited

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

On Tue, Aug 03, 2010 at 12:58:03PM -0700, David Fetter wrote:

On Mon, Aug 02, 2010 at 11:50:00PM -0600, Josh Tolley wrote:

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

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

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

I've added back one file in the patch enclosed here.  I'm still
getting compile fails from

CC="ccache gcc" ./configure     --prefix=$PG_PREFIX     --with-pgport=$PGPORT     --with-perl     --with-libxml     --enable-debug     --enable-cassert
make

Log from that also enclosed.

Yeah, I seem to have done a poor job of producing the patch based on the
repository I was working from. That said, it seems Pavel's working actively on
a patch anyway, so perhaps my updating the old one isn't all that worthwhile.
Pavel, is your code somewhere that we can get to it?

not now. please wait a week.

Regards

Pavel

Show quoted text

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

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

iEYEARECAAYFAkxYeiQACgkQRiRfCGf1UMPlEQCff+I4sCGtR+lzUs6Wb5JKi7Uu
3qYAnjLHzHzyMSHHX55QsphkaBbEJ0Zf
=uRqV
-----END PGP SIGNATURE-----

#8Joshua Tolley
eggyknap@gmail.com
In reply to: Pavel Stehule (#7)
Re: GROUPING SETS revisited

On Wed, Aug 04, 2010 at 04:44:05AM +0200, Pavel Stehule wrote:

Yeah, I seem to have done a poor job of producing the patch based on the
repository I was working from. That said, it seems Pavel's working actively on
a patch anyway, so perhaps my updating the old one isn't all that worthwhile.
Pavel, is your code somewhere that we can get to it?

not now. please wait a week.

That works for me. I'm glad to try doing a better job of putting together my
version of the patch, if anyone thinks it's useful, but it seems that since
Pavel's code is due to appear sometime in the foreseeable future, there's not
much point in my doing that.

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

#9Pavel Stehule
pavel.stehule@gmail.com
In reply to: Joshua Tolley (#8)
Re: GROUPING SETS revisited

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

On Wed, Aug 04, 2010 at 04:44:05AM +0200, Pavel Stehule wrote:

Yeah, I seem to have done a poor job of producing the patch based on the
repository I was working from. That said, it seems Pavel's working actively on
a patch anyway, so perhaps my updating the old one isn't all that worthwhile.
Pavel, is your code somewhere that we can get to it?

not now. please wait a week.

That works for me. I'm glad to try doing a better job of putting together my
version of the patch, if anyone thinks it's useful, but it seems that since
Pavel's code is due to appear sometime in the foreseeable future, there's not
much point in my doing that.

I hope, so next week you can do own work on this job - I am not a
native speaker, and my code will need a checking and fixing comments

Regards

Pavel

Show quoted text

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

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

iEYEARECAAYFAkxZkp8ACgkQRiRfCGf1UMMUcwCfcPayQbWRUYwhpCF1f24LsdD9
H/gAnRzCEq6yLX/RVLLi88ROhurOzbhK
=gUPx
-----END PGP SIGNATURE-----

#10Joshua Tolley
eggyknap@gmail.com
In reply to: Pavel Stehule (#9)
Re: GROUPING SETS revisited

On Thu, Aug 05, 2010 at 06:21:18AM +0200, Pavel Stehule wrote:

I hope, so next week you can do own work on this job - I am not a
native speaker, and my code will need a checking and fixing comments

I haven't entirely figured out how the code in the old patch works, but I
promise I *can* edit comments/docs :)

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

#11Pavel Stehule
pavel.stehule@gmail.com
In reply to: Joshua Tolley (#10)
1 attachment(s)
Re: GROUPING SETS revisited

Hello

I am sending a updated version.

i hope so there is more comments, longer and more descriptive
identifiers and I fixed a few bugs. But I found some new bugs :(

What is ok:

create table cars(name varchar, place varchar, count integer);
insert into cars values('skoda', 'czech rep.', 10000);
insert into cars values('skoda', 'germany', 5000);
insert into cars values('bmw', 'czech rep.', 100);
insert into cars values('bmw', 'germany', 1000);
insert into cars values('opel', 'czech rep.', 7000);
insert into cars values('opel', 'germany', 7000);

postgres=# select name, place, sum(count) from cars group by ();
name | place | sum
------+-------+-------
| | 30100
(1 row)

postgres=# select name, place, sum(count) from cars group by cube(name, place);
name | place | sum
-------+------------+-------
bmw | czech rep. | 100
skoda | germany | 5000
opel | czech rep. | 7000
opel | germany | 7000
skoda | czech rep. | 10000
bmw | germany | 1000
bmw | | 1100
skoda | | 15000
opel | | 14000
| germany | 13000
| czech rep. | 17100
| | 30100
(12 rows)

postgres=# select name, place, sum(count) from cars group by grouping
sets(name, place),();
name | place | sum
-------+------------+-------
bmw | | 1100
skoda | | 15000
opel | | 14000
| germany | 13000
| czech rep. | 17100
| | 30100
(6 rows)

postgres=# select name, place, sum(count) from cars group by grouping
sets(name, place,()),();
name | place | sum
-------+------------+-------
bmw | | 1100
skoda | | 15000
opel | | 14000
| germany | 13000
| czech rep. | 17100
| | 30100
(6 rows)

postgres=# select name, place, sum(count), grouping(name) from cars
group by grouping sets(name);
name | place | sum | grouping
-------+-------+-------+----------
bmw | | 1100 | 0
skoda | | 15000 | 0
opel | | 14000 | 0
(3 rows)

what is wrong:

postgres=# select name, place from cars group by ();
name | place
-------+------------
skoda | czech rep.
skoda | germany
bmw | czech rep.
bmw | germany
opel | czech rep.
opel | germany
(6 rows)

have to be NULL, NULL

postgres=# select name, place, sum(count), grouping(name) from cars
group by grouping sets(name) having grouping(name) = 1;
ERROR: unrecognized node type: 934

my rewriting rule is applied too late and maybe isn't optimal. I
replace a grouping(x) by const. maybe is better to use a variable.
Same issue is with ORDER BY clause.

So Joshua, can you look on code?

Regards

Pavel Stehule

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

Show quoted text

On Thu, Aug 05, 2010 at 06:21:18AM +0200, Pavel Stehule wrote:

I hope, so next week you can do own work on this job - I am not a
native speaker, and my code will need a checking and fixing comments

I haven't entirely figured out how the code in the old patch works, but I
promise I *can* edit comments/docs :)

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

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

iEYEARECAAYFAkxaSjEACgkQRiRfCGf1UMM9dQCZASYJUmXLe5i7L4aQnMicwMfy
cu8An3fMdR/ISezw5YV3KsCAOM+BILO1
=uZb+
-----END PGP SIGNATURE-----

Attachments:

verze002.difftext/x-patch; charset=US-ASCII; name=verze002.diffDownload
*** ./src/backend/nodes/copyfuncs.c.orig	2010-07-26 01:21:21.000000000 +0200
--- ./src/backend/nodes/copyfuncs.c	2010-08-02 19:14:28.120786164 +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-07-26 01:21:21.000000000 +0200
--- ./src/backend/nodes/equalfuncs.c	2010-08-02 19:17:38.057786174 +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/outfuncs.c.orig	2010-07-26 01:21:21.000000000 +0200
--- ./src/backend/nodes/outfuncs.c	2010-08-02 19:20:23.721785739 +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-02-16 23:34:43.000000000 +0100
--- ./src/backend/nodes/readfuncs.c	2010-08-02 19:23:33.522911219 +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/parser/analyze.c.orig	2010-08-05 12:54:54.343032233 +0200
--- ./src/backend/parser/analyze.c	2010-08-05 16:14:39.134031905 +0200
***************
*** 63,68 ****
--- 63,69 ----
  					 ExplainStmt *stmt);
  static void transformLockingClause(ParseState *pstate, Query *qry,
  					   LockingClause *lc, bool pushedDown);
+ static SelectStmt *transformGroupingSets(ParseState *pstate, SelectStmt *stmt);
  
  
  /*
***************
*** 178,183 ****
--- 179,186 ----
  		case T_SelectStmt:
  			{
  				SelectStmt *n = (SelectStmt *) parseTree;
+ 				
+ 				n = transformGroupingSets(pstate, n);
  
  				if (n->valuesLists)
  					result = transformValuesClause(pstate, n);
***************
*** 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);
--- 927,933 ----
  	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);
***************
*** 2242,2244 ****
--- 2245,2417 ----
  	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);
+ }
+ 
+ /*
+  * 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)
+ 				{
+ 					SelectStmt	*setop = makeSelectStmt(NIL, NIL);
+ 				
+ 					rarg = makeSelectStmt(copyObject(stmt->targetList),
+ 								list_make1(makeRangeVar(NULL, "GroupingSets", -1)));
+ 					rarg->havingClause = copyObject(stmt->havingClause);
+ 				
+ 					setop->op = SETOP_UNION;
+ 					setop->larg = larg;
+ 					setop->rarg = rarg;
+ 					setop->all = all;
+ 					
+ 					larg = setop;
+ 				}
+ 				/* merge larg to result */
+ 				cteedstmt->op = larg->op;
+ 				cteedstmt->larg = larg->larg;
+ 				cteedstmt->rarg = larg->rarg;
+ 				cteedstmt->all = larg->all;
+ 			}
+ 			else 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);
+ 					
+ 				}
+ 			}
+ 		
+ 			((SelectStmt *)cte->ctequery)->targetList = makeStarTargetList();
+ 			((SelectStmt *)cte->ctequery)->groupClause = NULL;
+ 			((SelectStmt *)cte->ctequery)->sortClause = NIL;
+ 			((SelectStmt *)cte->ctequery)->limitOffset = stmt->limitOffset;
+ 			((SelectStmt *)cte->ctequery)->limitCount = stmt->limitCount;
+ 			((SelectStmt *)cte->ctequery)->lockingClause = stmt->lockingClause;
+ 		
+ 			return cteedstmt;
+ 		}
+ 		else
+ 			/* trim GroupByClause to groupByClause */
+ 			stmt->groupClause = (Node *)((GroupBy *)stmt->groupClause)->grouping_sets;
+ 	}
+ 
+ 	return stmt;
+ }
*** ./src/backend/parser/gram.y.orig	2010-07-26 01:21:21.000000000 +0200
--- ./src/backend/parser/gram.y	2010-08-05 13:48:40.565033096 +0200
***************
*** 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
+ %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*/
! 				{
! 					$$ = NIL;
! 				}
! 		;
! 
! 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,9970 ----
  					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 processed differently - depends on
+ 					 * context - it can be used as Grouping Sets Operator (inside
+ 					 * grouping sets specification) or it can be used as FuncCall.
+ 					 * CUBE is keyword in ANSI SQL, but it cannot be a reserved 
+ 					 * keyword, because contrib "cube" module uses it. There isn't
+ 					 * reason to process ROLLUP diferently than CUBE.
+ 					 */
+ 					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 ****
--- 11134,11140 ----
  			| SERVER
  			| SESSION
  			| SET
+ 			| SETS
  			| SHARE
  			| SHOW
  			| SIMPLE
***************
*** 11120,11125 ****
--- 11212,11218 ----
  			| EXTRACT
  			| FLOAT_P
  			| GREATEST
+ 			| GROUPING_ID
  			| INOUT
  			| INT_P
  			| INTEGER
***************
*** 11214,11219 ****
--- 11307,11313 ----
  			| COLUMN
  			| CONSTRAINT
  			| CREATE
+ 			| CUBE
  			| CURRENT_CATALOG
  			| CURRENT_DATE
  			| CURRENT_ROLE
***************
*** 11235,11240 ****
--- 11329,11335 ----
  			| FROM
  			| GRANT
  			| GROUP_P
+ 			| GROUPING
  			| HAVING
  			| IN_P
  			| INITIALLY
***************
*** 11256,11261 ****
--- 11351,11357 ----
  			| PRIMARY
  			| REFERENCES
  			| RETURNING
+ 			| ROLLUP
  			| SELECT
  			| SESSION_USER
  			| SOME
*** ./src/backend/parser/Makefile.orig	2010-01-05 04:56:52.000000000 +0100
--- ./src/backend/parser/Makefile	2010-08-05 08:30:53.039043502 +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-03-17 17:52:38.000000000 +0100
--- ./src/backend/parser/parse_agg.c	2010-08-05 16:09:45.804033853 +0200
***************
*** 34,43 ****
--- 34,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);
  
  
  /*
***************
*** 397,402 ****
--- 405,422 ----
  		}
  	}
  
+ 	/* 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);
+ 		/* 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,
--- 425,430 ----
***************
*** 741,743 ****
--- 760,835 ----
  										 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 (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);
+ }
+ 
+ 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);
+ }
*** ./src/backend/parser/parse_expr.c.orig	2010-07-06 21:18:57.000000000 +0200
--- ./src/backend/parser/parse_expr.c	2010-08-05 13:29:22.273031825 +0200
***************
*** 293,298 ****
--- 293,357 ----
  			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, we have to do decision later by context. This
+ 				 * transformation replace unevaluated gs operators by typical funccall.
+ 				 * "cube" cannot be a reserved keyword. Contrib module contains a few 
+ 				 * functions "cube".
+ 				 */
+ 				if (gsf->kind == GROUPINGSETS_FUNCTION_CUBE || gsf->kind == GROUPINGSETS_FUNCTION_ROLLUP)
+ 				{
+ 					FuncCall *fc = makeNode(FuncCall);
+ 					
+ 					if (gsf->kind == GROUPINGSETS_FUNCTION_CUBE)
+ 					{
+ 						/* for cube() */
+ 						fc->funcname = list_make1(makeString("cube"));
+ 					}
+ 					else
+ 					{
+ 						/* for rollup() */
+ 						fc->funcname = list_make1(makeString("rollup"));
+ 					}
+ 					
+ 					fc->args = gsf->expr_list;
+ 					fc->agg_order = NIL;
+ 					fc->agg_star = false;
+ 					fc->agg_distinct = false;
+ 					fc->func_variadic = false;
+ 					fc->over = NULL;
+ 					fc->location = gsf->location;
+ 					result = transformFuncCall(pstate, (FuncCall *) fc);
+ 				}
+ 				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 ****
--- 383,389 ----
  		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-03 14:24:18.042985208 +0200
--- ./src/backend/parser/parse_gsets.c	2010-08-05 15:09:44.722157284 +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-02-26 03:00:52.000000000 +0100
--- ./src/backend/parser/parse_target.c	2010-08-05 13:18:04.358157078 +0200
***************
*** 1586,1591 ****
--- 1586,1602 ----
  		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;
+ 			}
+ 			break;
  		default:
  			break;
  	}
*** ./src/include/nodes/nodes.h.orig	2010-07-12 19:01:06.000000000 +0200
--- ./src/include/nodes/nodes.h	2010-08-02 19:13:55.583786368 +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-07-26 01:21:22.000000000 +0200
--- ./src/include/nodes/parsenodes.h	2010-08-05 12:51:43.718032608 +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-02-12 18:33:21.000000000 +0100
--- ./src/include/parser/kwlist.h	2010-08-02 15:00:33.799910696 +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-02-26 03:01:26.000000000 +0100
--- ./src/include/parser/parse_node.h	2010-08-05 10:55:19.581032166 +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 */
#12Joshua Tolley
eggyknap@gmail.com
In reply to: Pavel Stehule (#11)
Re: GROUPING SETS revisited

On Thu, Aug 05, 2010 at 04:46:51PM +0200, Pavel Stehule wrote:

So Joshua, can you look on code?

Sure... thanks :)

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

#13Pavel Stehule
pavel.stehule@gmail.com
In reply to: Joshua Tolley (#12)
Re: GROUPING SETS revisited

I found other issue :(

postgres=# select name, place from cars group by grouping sets(name, place,());
name | place
-------+------------
bmw |
skoda |
opel |
| germany
| czech rep.
skoda | czech rep.
skoda | germany
bmw | czech rep.
bmw | germany
opel | czech rep.
opel | germany
(11 rows)

postgres=# explain select name, place from cars group by grouping
sets(name, place,());
QUERY PLAN
------------------------------------------------------------------------------
Append (cost=36.98..88.55 rows=1230 width=54)
CTE GroupingSets
-> Seq Scan on cars (cost=0.00..18.30 rows=830 width=68)
-> HashAggregate (cost=18.68..20.68 rows=200 width=32)
-> CTE Scan on "GroupingSets" (cost=0.00..16.60 rows=830 width=32)
-> HashAggregate (cost=18.68..20.68 rows=200 width=32)
-> CTE Scan on "GroupingSets" (cost=0.00..16.60 rows=830 width=32)
-> CTE Scan on "GroupingSets" (cost=0.00..16.60 rows=830 width=64)
(8 rows)

the combination of nonagregates and empty sets do a problems - because
we can't ensure agg mode without aggregates or group by. But it is
only minor issue

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

Show quoted text

On Thu, Aug 05, 2010 at 04:46:51PM +0200, Pavel Stehule wrote:

So Joshua, can you look on code?

Sure... thanks :)

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

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

iEYEARECAAYFAkxa1NsACgkQRiRfCGf1UMPwzQCgjz52P86Yx4ac4aRkKwjn8OHK
6/EAoJ/CjXEyPaLpx39SI5bKQPz+AwBR
=Mi2J
-----END PGP SIGNATURE-----

#14Joshua Tolley
eggyknap@gmail.com
In reply to: Pavel Stehule (#11)
Re: GROUPING SETS revisited

On Thu, Aug 05, 2010 at 04:46:51PM +0200, Pavel Stehule wrote:

I am sending a updated version.

I've been looking at the changes to gram.y, and noted the comment under func_expr
where you added CUBE and ROLLUP definitions. It says that CUBE can't be a
reserved keyword because it's already used in the cube contrib module. But
then the changes to kwlist.h include this:

+ PG_KEYWORD("cube", CUBE, RESERVED_KEYWORD)
...
+ PG_KEYWORD("rollup", ROLLUP, RESERVED_KEYWORD)

...and CUBE and ROLLUP are added in gram.y under the reserved_keyword list. I
realize things like CURRENT_TIME, that also have special entries in the
func_expr grammar, are also reserved keywords, but this all seems at odds with
the comment. What am I missing? Is the comment simply pointing out that the
designation of CUBE and ROLLUP as reserved keywords will have to change at
some point, but it hasn't been implemented yet (or no one has figured out how
to do it)?

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

#15Pavel Stehule
pavel.stehule@gmail.com
In reply to: Joshua Tolley (#14)
Re: GROUPING SETS revisited

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

On Thu, Aug 05, 2010 at 04:46:51PM +0200, Pavel Stehule wrote:

I am sending a updated version.

I've been looking at the changes to gram.y, and noted the comment under func_expr
where you added CUBE and ROLLUP definitions. It says that CUBE can't be a
reserved keyword because it's already used in the cube contrib module. But
then the changes to kwlist.h include this:

I am little bit confused now - it's bad comment - and I have to verify
it. What I remember, we cannot to use a two parser's rules, because it
going to a conflict. So there have to be used a trick with a moving to
decision to transform stage, where we have a context info. I have to
recheck a minimal level - probably it can't be a RESERVED_KEYWORD.
Because then we can't to create a function "cube".

Show quoted text
+ PG_KEYWORD("cube", CUBE, RESERVED_KEYWORD)
...
+ PG_KEYWORD("rollup", ROLLUP, RESERVED_KEYWORD)

...and CUBE and ROLLUP are added in gram.y under the reserved_keyword list. I
realize things like CURRENT_TIME, that also have special entries in the
func_expr grammar, are also reserved keywords, but this all seems at odds with
the comment. What am I missing? Is the comment simply pointing out that the
designation of CUBE and ROLLUP as reserved keywords will have to change at
some point, but it hasn't been implemented yet (or no one has figured out how
to do it)?

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

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

iEYEARECAAYFAkxcjSIACgkQRiRfCGf1UMPpCwCcCHBh/1NiLykIcVYgPyfbIegF
xq0AoID75rCPiW8yf29OSkaJVza1FQt5
=PcLs
-----END PGP SIGNATURE-----

#16Pavel Stehule
pavel.stehule@gmail.com
In reply to: Pavel Stehule (#15)
Re: GROUPING SETS revisited

Hello

I was confused when I though so I found a solution of 1 shift/reduce conflict :(

All identificators used for buildin functions have to be a
col_name_keywords or reserved keyword. There is conflict with our
(probably obsolete) feature SELECT colname(tabname). So for this
moment the real solution is removing CUBE and ROLLUP from keywords and
dynamically testing a funcname in transformation stage - what is
slower and more ugly.

ideas?

Regards

Pavel Stehule

2010/8/7 Pavel Stehule <pavel.stehule@gmail.com>:

Show quoted text

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

On Thu, Aug 05, 2010 at 04:46:51PM +0200, Pavel Stehule wrote:

I am sending a updated version.

I've been looking at the changes to gram.y, and noted the comment under func_expr
where you added CUBE and ROLLUP definitions. It says that CUBE can't be a
reserved keyword because it's already used in the cube contrib module. But
then the changes to kwlist.h include this:

I am little bit confused now - it's bad comment - and I have to verify
it. What I remember, we cannot to use a two parser's rules, because it
going to a conflict. So there have to be used a trick with a moving to
decision to transform stage, where we have a context info. I have to
recheck a minimal level - probably it can't be a RESERVED_KEYWORD.
Because then we can't to create a function "cube".

+ PG_KEYWORD("cube", CUBE, RESERVED_KEYWORD)
...
+ PG_KEYWORD("rollup", ROLLUP, RESERVED_KEYWORD)

...and CUBE and ROLLUP are added in gram.y under the reserved_keyword list. I
realize things like CURRENT_TIME, that also have special entries in the
func_expr grammar, are also reserved keywords, but this all seems at odds with
the comment. What am I missing? Is the comment simply pointing out that the
designation of CUBE and ROLLUP as reserved keywords will have to change at
some point, but it hasn't been implemented yet (or no one has figured out how
to do it)?

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

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

iEYEARECAAYFAkxcjSIACgkQRiRfCGf1UMPpCwCcCHBh/1NiLykIcVYgPyfbIegF
xq0AoID75rCPiW8yf29OSkaJVza1FQt5
=PcLs
-----END PGP SIGNATURE-----

#17Pavel Stehule
pavel.stehule@gmail.com
In reply to: Hitoshi Harada (#3)
Re: GROUPING SETS revisited

Hello

I found a break in GROUPING SETS implementation. Now I am playing with
own executor and planner node and I can't to go forward :(. Probably
this feature will need a significant update of our agg implementation.
Probably needs a some similar structure like CTE but it can be a
little bit reduced - there are a simple relation between source query
and result query - I am not sure, if this has to be implemented via
subqueries? The second question is relative big differencies between
GROUP BY behave and GROUP BY GROUPING SETS behave. Now I don't know
about way to join GROUP BY and GROUPING SETS together

Any ideas welcome

Regards

Pavel