gs91_02.patch

text/plain

Filename: gs91_02.patch
Type: text/plain
Part: 0
Message: Re: GROUPING SETS revisited

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
src/backend/parser/analyze.c 162 2
src/backend/parser/gram.y 102 9
src/backend/parser/Makefile 1 1
src/backend/parser/parse_agg.c 95 1
src/backend/parser/parse_expr.c 20 0
src/backend/parser/parse_target.c 16 0
src/include/nodes/nodes.h 4 0
src/include/nodes/parsenodes.h 48 1
src/include/parser/kwlist.h 5 0
src/include/parser/parse_gsets.h 12 0
src/include/parser/parse_node.h 1 0
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;
 
 	/*