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 -- Joshua Tolley / eggyknap End Point Corporation http://www.endpoint.com
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; /*
signature.asc
Description: Digital signature