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 opt_existing_window_name +%type grouping_element empty_grouping_set grouping_sets_spec + group_clause +%type grouping_element_list +%type 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; /*