gs91_02.patch
text/plain
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;
/*