*** ./src/backend/nodes/copyfuncs.c.orig 2010-07-26 01:21:21.000000000 +0200 --- ./src/backend/nodes/copyfuncs.c 2010-08-02 19:14:28.120786164 +0200 *************** *** 2089,2094 **** --- 2089,2141 ---- return newnode; } + static GroupBy * + _copyGroupBy(GroupBy *from) + { + GroupBy *newnode = makeNode(GroupBy); + + COPY_SCALAR_FIELD(all); + COPY_NODE_FIELD(grouping_sets); + COPY_LOCATION_FIELD(location); + + return newnode; + } + + static GroupingSetsFunction * + _copyGroupingSetsFunction(GroupingSetsFunction *from) + { + GroupingSetsFunction *newnode = makeNode(GroupingSetsFunction); + + COPY_SCALAR_FIELD(kind); + COPY_NODE_FIELD(expr); + COPY_NODE_FIELD(expr_list); + COPY_LOCATION_FIELD(location); + + return newnode; + } + + static GroupingSetsSpecification * + _copyGroupingSetsSpecification(GroupingSetsSpecification *from) + { + GroupingSetsSpecification *newnode = makeNode(GroupingSetsSpecification); + + COPY_NODE_FIELD(grouping_sets); + COPY_SCALAR_FIELD(has_empty_set); + COPY_LOCATION_FIELD(location); + + return newnode; + } + + static GroupingSetsEmptySet * + _copyGroupingSetsEmptySet(GroupingSetsEmptySet *from) + { + GroupingSetsEmptySet *newnode = makeNode(GroupingSetsEmptySet); + + COPY_LOCATION_FIELD(location); + + return newnode; + } + static WindowDef * _copyWindowDef(WindowDef *from) { *************** *** 4208,4213 **** --- 4255,4272 ---- case T_SortBy: retval = _copySortBy(from); break; + case T_GroupBy: + retval = _copyGroupBy(from); + break; + case T_GroupingSetsFunction: + retval = _copyGroupingSetsFunction(from); + break; + case T_GroupingSetsSpecification: + retval = _copyGroupingSetsSpecification(from); + break; + case T_GroupingSetsEmptySet: + retval = _copyGroupingSetsEmptySet(from); + break; case T_WindowDef: retval = _copyWindowDef(from); break; *** ./src/backend/nodes/equalfuncs.c.orig 2010-07-26 01:21:21.000000000 +0200 --- ./src/backend/nodes/equalfuncs.c 2010-08-02 19:17:38.057786174 +0200 *************** *** 2052,2057 **** --- 2052,2096 ---- } static bool + _equalGroupBy(GroupBy *a, GroupBy *b) + { + COMPARE_SCALAR_FIELD(all); + COMPARE_NODE_FIELD(grouping_sets); + COMPARE_LOCATION_FIELD(location); + + return true; + } + + static bool + _equalGroupingSetsFunction(GroupingSetsFunction *a, GroupingSetsFunction *b) + { + COMPARE_SCALAR_FIELD(kind); + COMPARE_NODE_FIELD(expr); + COMPARE_NODE_FIELD(expr_list); + COMPARE_LOCATION_FIELD(location); + + return true; + } + + static bool + _equalGroupingSetsSpecification(GroupingSetsSpecification *a, GroupingSetsSpecification *b) + { + COMPARE_NODE_FIELD(grouping_sets); + COMPARE_SCALAR_FIELD(has_empty_set); + COMPARE_LOCATION_FIELD(location); + + return true; + } + + static bool + _equalGroupingSetsEmptySet(GroupingSetsEmptySet *a, GroupingSetsEmptySet *b) + { + COMPARE_LOCATION_FIELD(location); + + return true; + } + + static bool _equalWindowDef(WindowDef *a, WindowDef *b) { COMPARE_STRING_FIELD(name); *************** *** 2875,2880 **** --- 2914,2931 ---- case T_SortBy: retval = _equalSortBy(a, b); break; + case T_GroupBy: + retval = _equalGroupBy(a, b); + break; + case T_GroupingSetsFunction: + retval = _equalGroupingSetsFunction(a, b); + break; + case T_GroupingSetsSpecification: + retval = _equalGroupingSetsSpecification(a, b); + break; + case T_GroupingSetsEmptySet: + retval = _equalGroupingSetsEmptySet(a, b); + break; case T_WindowDef: retval = _equalWindowDef(a, b); break; *** ./src/backend/nodes/outfuncs.c.orig 2010-07-26 01:21:21.000000000 +0200 --- ./src/backend/nodes/outfuncs.c 2010-08-02 19:20:23.721785739 +0200 *************** *** 2327,2332 **** --- 2327,2371 ---- } static void + _outGroupBy(StringInfo str, GroupBy *node) + { + WRITE_NODE_TYPE("GROUPBY"); + + WRITE_BOOL_FIELD(all); + WRITE_NODE_FIELD(grouping_sets); + WRITE_LOCATION_FIELD(location); + } + + static void + _outGroupingSetsFunction(StringInfo str, GroupingSetsFunction *node) + { + WRITE_NODE_TYPE("GS_FUNCTION"); + + WRITE_ENUM_FIELD(kind, GroupingSets_func_kind); + WRITE_NODE_FIELD(expr); + WRITE_NODE_FIELD(expr_list); + WRITE_LOCATION_FIELD(location); + } + + static void + _outGroupingSetsSpecification(StringInfo str, GroupingSetsSpecification *node) + { + WRITE_NODE_TYPE("GS_SPECIFICATION"); + + WRITE_NODE_FIELD(grouping_sets); + WRITE_BOOL_FIELD(has_empty_set); + WRITE_LOCATION_FIELD(location); + } + + static void + _outGroupingSetsEmptySet(StringInfo str, GroupingSetsEmptySet *node) + { + WRITE_NODE_TYPE("GS_EMPTY_SET"); + + WRITE_LOCATION_FIELD(location); + } + + static void _outWindowDef(StringInfo str, WindowDef *node) { WRITE_NODE_TYPE("WINDOWDEF"); *************** *** 2881,2886 **** --- 2920,2937 ---- case T_SortBy: _outSortBy(str, obj); break; + case T_GroupBy: + _outGroupBy(str, obj); + break; + case T_GroupingSetsFunction: + _outGroupingSetsFunction(str, obj); + break; + case T_GroupingSetsSpecification: + _outGroupingSetsSpecification(str, obj); + break; + case T_GroupingSetsEmptySet: + _outGroupingSetsEmptySet(str, obj); + break; case T_WindowDef: _outWindowDef(str, obj); break; *** ./src/backend/nodes/readfuncs.c.orig 2010-02-16 23:34:43.000000000 +0100 --- ./src/backend/nodes/readfuncs.c 2010-08-02 19:23:33.522911219 +0200 *************** *** 268,273 **** --- 268,323 ---- } /* + * _readGroupBy + */ + static GroupBy * + _readGroupBy(void) + { + READ_LOCALS(GroupBy); + + READ_BOOL_FIELD(all); + READ_NODE_FIELD(grouping_sets); + READ_LOCATION_FIELD(location); + + READ_DONE(); + } + + static GroupingSetsFunction * + _readGroupingSetsFunction(void) + { + READ_LOCALS(GroupingSetsFunction); + + READ_ENUM_FIELD(kind, GroupingSets_func_kind); + READ_NODE_FIELD(expr); + READ_NODE_FIELD(expr_list); + READ_LOCATION_FIELD(location); + + READ_DONE(); + } + + static GroupingSetsSpecification * + _readGroupingSetsSpecification(void) + { + READ_LOCALS(GroupingSetsSpecification); + + READ_NODE_FIELD(grouping_sets); + READ_BOOL_FIELD(has_empty_set); + READ_LOCATION_FIELD(location); + + READ_DONE(); + } + + static GroupingSetsEmptySet * + _readGroupingSetsEmptySet(void) + { + READ_LOCALS(GroupingSetsEmptySet); + + READ_LOCATION_FIELD(location); + + READ_DONE(); + } + + /* * _readWindowClause */ static WindowClause * *************** *** 1200,1205 **** --- 1250,1263 ---- return_value = _readQuery(); else if (MATCH("SORTGROUPCLAUSE", 15)) return_value = _readSortGroupClause(); + else if (MATCH("GROUPBY", 7)) + return_value = _readGroupBy(); + else if (MATCH("GS_FUNCTION", 11)) + return_value = _readGroupingSetsFunction(); + else if (MATCH("GS_SPECIFICATION", 16)) + return_value = _readGroupingSetsSpecification(); + else if (MATCH("GS_EMPTY_SET", 12)) + return_value = _readGroupingSetsEmptySet(); else if (MATCH("WINDOWCLAUSE", 12)) return_value = _readWindowClause(); else if (MATCH("ROWMARKCLAUSE", 13)) *** ./src/backend/parser/analyze.c.orig 2010-08-05 12:54:54.343032233 +0200 --- ./src/backend/parser/analyze.c 2010-08-05 16:14:39.134031905 +0200 *************** *** 63,68 **** --- 63,69 ---- ExplainStmt *stmt); static void transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, bool pushedDown); + static SelectStmt *transformGroupingSets(ParseState *pstate, SelectStmt *stmt); /* *************** *** 178,183 **** --- 179,186 ---- case T_SelectStmt: { SelectStmt *n = (SelectStmt *) parseTree; + + n = transformGroupingSets(pstate, n); if (n->valuesLists) result = transformValuesClause(pstate, n); *************** *** 924,930 **** Assert(stmt->targetList == NIL); Assert(stmt->fromClause == NIL); Assert(stmt->whereClause == NULL); ! Assert(stmt->groupClause == NIL); Assert(stmt->havingClause == NULL); Assert(stmt->windowClause == NIL); Assert(stmt->op == SETOP_NONE); --- 927,933 ---- Assert(stmt->targetList == NIL); Assert(stmt->fromClause == NIL); Assert(stmt->whereClause == NULL); ! Assert(stmt->groupClause == NULL); Assert(stmt->havingClause == NULL); Assert(stmt->windowClause == NIL); Assert(stmt->op == SETOP_NONE); *************** *** 2242,2244 **** --- 2245,2417 ---- qry->rowMarks = lappend(qry->rowMarks, rc); } + + /* + * generate parser's node for simple SELECT + */ + static SelectStmt * + makeSelectStmt(List *targetList, List *fromClause) + { + SelectStmt *n = makeNode(SelectStmt); + n->distinctClause = NULL; + n->intoClause = NULL; + n->targetList = targetList; + n->fromClause = fromClause; + n->whereClause = NULL; + n->groupClause = NULL; + n->havingClause = NULL; + n->windowClause = NIL; + n->withClause = NULL; + n->valuesLists = NIL; + n->sortClause = NIL; + n->limitOffset = NULL; + n->limitCount = NULL; + n->lockingClause = NIL; + n->op = SETOP_NONE; + n->all = false; + n->larg = NULL; + n->rarg = NULL; + return n; + } + + /* + * generate parser's node for star target + */ + static List * + makeStarTargetList(void) + { + ResTarget *rt = makeNode(ResTarget); + + rt->name = NULL; + rt->indirection = NIL; + rt->val = (Node *) makeNode(ColumnRef); + ((ColumnRef *) rt->val)->fields = list_make1(makeNode(A_Star)); + rt->location = -1; + + return list_make1(rt); + } + + /* + * transform a GROUP BY GROUPING SETS to CTE + */ + static SelectStmt * + transformGroupingSets(ParseState *pstate, SelectStmt *stmt) + { + if (stmt->groupClause && IsA(stmt->groupClause, GroupBy)) + { + GroupingSetsSpecification *gss = (GroupingSetsSpecification *) evalGroupingSets(pstate, + (List *)((GroupBy *)stmt->groupClause)->grouping_sets); + + if (pstate->p_hasGroupingSets) + { + CommonTableExpr *cte = makeNode(CommonTableExpr); + SelectStmt *cteedstmt; + int ngroupingsets = list_length(gss->grouping_sets) + (gss->has_empty_set ? 1 : 0); + bool all = ((GroupBy *) stmt->groupClause)->all; + + cteedstmt = makeSelectStmt(NIL, NIL); + cteedstmt->intoClause = stmt->intoClause; + cteedstmt->sortClause = stmt->sortClause; + cteedstmt->limitOffset = stmt->limitOffset; + cteedstmt->limitCount = stmt->limitCount; + cteedstmt->lockingClause = stmt->lockingClause; + + cte->ctename = "GroupingSets"; + cte->ctequery = (Node *) stmt; + cte->location = -1; + + cteedstmt->withClause = makeNode(WithClause); + cteedstmt->withClause->ctes = list_make1(cte); + cteedstmt->withClause->recursive = false; + cteedstmt->withClause->location = -1; + + /* when is more than one grouping set, then we should generate setop node */ + if (ngroupingsets > 1) + { + /* add quuery under union all for every grouping set */ + SelectStmt *larg = NULL; + SelectStmt *rarg; + ListCell *lc; + + foreach(lc, gss->grouping_sets) + { + List *groupClause; + + Assert(IsA(lfirst(lc), List)); + groupClause = (List *) lfirst(lc); + + if (larg == NULL) + { + larg = makeSelectStmt(copyObject(stmt->targetList), + list_make1(makeRangeVar(NULL, "GroupingSets", -1))); + larg->groupClause = (Node *) groupClause; + larg->havingClause = copyObject(stmt->havingClause); + } + else + { + SelectStmt *setop = makeSelectStmt(NIL, NIL); + + rarg = makeSelectStmt(copyObject(stmt->targetList), + list_make1(makeRangeVar(NULL, "GroupingSets", -1))); + rarg->groupClause = (Node *) groupClause; + rarg->havingClause = copyObject(stmt->havingClause); + + setop->op = SETOP_UNION; + setop->larg = larg; + setop->rarg = rarg; + setop->all = all; + + larg = setop; + } + } + if (gss->has_empty_set) + { + SelectStmt *setop = makeSelectStmt(NIL, NIL); + + rarg = makeSelectStmt(copyObject(stmt->targetList), + list_make1(makeRangeVar(NULL, "GroupingSets", -1))); + rarg->havingClause = copyObject(stmt->havingClause); + + setop->op = SETOP_UNION; + setop->larg = larg; + setop->rarg = rarg; + setop->all = all; + + larg = setop; + } + /* merge larg to result */ + cteedstmt->op = larg->op; + cteedstmt->larg = larg->larg; + cteedstmt->rarg = larg->rarg; + cteedstmt->all = larg->all; + } + else if (ngroupingsets == 1) + { + /* there isn't used setop node */ + cteedstmt->targetList = copyObject(stmt->targetList); + cteedstmt->fromClause = list_make1(makeRangeVar(NULL, "GroupingSets", -1)); + cteedstmt->havingClause = copyObject(stmt->havingClause); + + if (gss->grouping_sets != NIL) + { + cteedstmt->groupClause = linitial(gss->grouping_sets); + + } + } + + ((SelectStmt *)cte->ctequery)->targetList = makeStarTargetList(); + ((SelectStmt *)cte->ctequery)->groupClause = NULL; + ((SelectStmt *)cte->ctequery)->sortClause = NIL; + ((SelectStmt *)cte->ctequery)->limitOffset = stmt->limitOffset; + ((SelectStmt *)cte->ctequery)->limitCount = stmt->limitCount; + ((SelectStmt *)cte->ctequery)->lockingClause = stmt->lockingClause; + + return cteedstmt; + } + else + /* trim GroupByClause to groupByClause */ + stmt->groupClause = (Node *)((GroupBy *)stmt->groupClause)->grouping_sets; + } + + return stmt; + } *** ./src/backend/parser/gram.y.orig 2010-07-26 01:21:21.000000000 +0200 --- ./src/backend/parser/gram.y 2010-08-05 13:48:40.565033096 +0200 *************** *** 437,442 **** --- 437,446 ---- opt_frame_clause frame_extent frame_bound %type opt_existing_window_name + %type gsets_element gsets_empty_set gsets_specification + %type gsets_element_list + %type opt_gsets_quantifier + /* * Non-keyword token types. These are hard-wired into the "flex" lexer. *************** *** 471,477 **** CLUSTER COALESCE COLLATE COLUMN COMMENT COMMENTS COMMIT COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB ! CREATEROLE CREATEUSER CROSS CSV CURRENT_P CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE --- 475,481 ---- CLUSTER COALESCE COLLATE COLUMN COMMENT COMMENTS COMMIT COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB ! CREATEROLE CREATEUSER CROSS CSV CUBE CURRENT_P CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE *************** *** 485,491 **** FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS ! GLOBAL GRANT GRANTED GREATEST GROUP_P HANDLER HAVING HEADER_P HOLD HOUR_P --- 489,495 ---- FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS ! GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING GROUPING_ID HANDLER HAVING HEADER_P HOLD HOUR_P *************** *** 519,528 **** RANGE READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART ! RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES ! SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE_P START STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P SYMMETRIC SYSID SYSTEM_P --- 523,532 ---- RANGE READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART ! RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP ROW ROWS RULE SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES ! SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SETS SHARE SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE_P START STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P SYMMETRIC SYSID SYSTEM_P *************** *** 7795,7804 **** | NEXT { $$ = 0; } ; - group_clause: ! GROUP_P BY expr_list { $$ = $3; } ! | /*EMPTY*/ { $$ = NIL; } ; having_clause: --- 7799,7851 ---- | NEXT { $$ = 0; } ; group_clause: ! GROUP_P BY opt_gsets_quantifier gsets_element_list ! { ! GroupBy *n = makeNode(GroupBy); ! n->all = $3; ! n->grouping_sets = $4; ! n->location = @1; ! $$ = (Node *) n; ! } ! | /*EMPTY*/ ! { ! $$ = NIL; ! } ! ; ! ! gsets_specification: ! GROUPING SETS '(' gsets_element_list ')' ! { ! GroupingSetsSpecification *gss = makeNode(GroupingSetsSpecification); ! gss->grouping_sets = $4; ! gss->has_empty_set = false; /* not known now */ ! gss->location = @1; ! $$ = (Node *) gss; ! } ! ! gsets_element_list: ! gsets_element { $$ = list_make1($1); } ! | gsets_element_list ',' gsets_element { $$ = lappend($1, $3); } ! ; ! ! gsets_element: ! a_expr { $$ = $1; } ! | gsets_specification { $$ = $1; } ! | gsets_empty_set { $$ = $1; } ! ; ! ! gsets_empty_set: ! '(' ')' ! { ! $$ = (Node *) makeNode(GroupingSetsEmptySet); ! } ! ; ! ! opt_gsets_quantifier: ! ALL { $$ = true; } ! | DISTINCT { $$ = false; } ! | /*EMPTY*/ { $$ = true; } ; having_clause: *************** *** 9874,9879 **** --- 9921,9970 ---- n->location = @1; $$ = (Node *)n; } + | GROUPING '(' a_expr ')' + { + GroupingSetsFunction *gsf = makeNode(GroupingSetsFunction); + gsf->kind = GROUPINGSETS_FUNCTION_GROUPING; + gsf->expr = $3; + gsf->expr_list = NIL; + gsf->location = @1; + $$ = (Node *) gsf; + } + | GROUPING_ID '(' expr_list ')' + { + GroupingSetsFunction *gsf = makeNode(GroupingSetsFunction); + gsf->kind = GROUPINGSETS_FUNCTION_GROUPING_ID; + gsf->expr = NULL; + gsf->expr_list = $3; + gsf->location = @1; + $$ = (Node *) gsf; + } + | CUBE '(' expr_list ')' + { + /* + * Cube() and Rollup() are processed differently - depends on + * context - it can be used as Grouping Sets Operator (inside + * grouping sets specification) or it can be used as FuncCall. + * CUBE is keyword in ANSI SQL, but it cannot be a reserved + * keyword, because contrib "cube" module uses it. There isn't + * reason to process ROLLUP diferently than CUBE. + */ + GroupingSetsFunction *gsf = makeNode(GroupingSetsFunction); + gsf->kind = GROUPINGSETS_FUNCTION_CUBE; + gsf->expr = NULL; + gsf->expr_list = $3; + gsf->location = @1; + $$ = (Node *) gsf; + } + | ROLLUP '(' expr_list ')' + { + GroupingSetsFunction *gsf = makeNode(GroupingSetsFunction); + gsf->kind = GROUPINGSETS_FUNCTION_ROLLUP; + gsf->expr = NULL; + gsf->expr_list = $3; + gsf->location = @1; + $$ = (Node *) gsf; + } ; /* *************** *** 11043,11048 **** --- 11134,11140 ---- | SERVER | SESSION | SET + | SETS | SHARE | SHOW | SIMPLE *************** *** 11120,11125 **** --- 11212,11218 ---- | EXTRACT | FLOAT_P | GREATEST + | GROUPING_ID | INOUT | INT_P | INTEGER *************** *** 11214,11219 **** --- 11307,11313 ---- | COLUMN | CONSTRAINT | CREATE + | CUBE | CURRENT_CATALOG | CURRENT_DATE | CURRENT_ROLE *************** *** 11235,11240 **** --- 11329,11335 ---- | FROM | GRANT | GROUP_P + | GROUPING | HAVING | IN_P | INITIALLY *************** *** 11256,11261 **** --- 11351,11357 ---- | PRIMARY | REFERENCES | RETURNING + | ROLLUP | SELECT | SESSION_USER | SOME *** ./src/backend/parser/Makefile.orig 2010-01-05 04:56:52.000000000 +0100 --- ./src/backend/parser/Makefile 2010-08-05 08:30:53.039043502 +0200 *************** *** 15,21 **** OBJS= analyze.o gram.o keywords.o kwlookup.o parser.o \ parse_agg.o parse_clause.o parse_coerce.o parse_cte.o parse_expr.o \ parse_func.o parse_node.o parse_oper.o parse_param.o parse_relation.o \ ! parse_target.o parse_type.o parse_utilcmd.o scansup.o FLEXFLAGS = -CF --- 15,21 ---- OBJS= analyze.o gram.o keywords.o kwlookup.o parser.o \ parse_agg.o parse_clause.o parse_coerce.o parse_cte.o parse_expr.o \ parse_func.o parse_node.o parse_oper.o parse_param.o parse_relation.o \ ! parse_target.o parse_type.o parse_utilcmd.o scansup.o parse_gsets.o FLEXFLAGS = -CF *** ./src/backend/parser/parse_agg.c.orig 2010-03-17 17:52:38.000000000 +0100 --- ./src/backend/parser/parse_agg.c 2010-08-05 16:09:45.804033853 +0200 *************** *** 34,43 **** --- 34,51 ---- int sublevels_up; } check_ungrouped_columns_context; + typedef struct + { + ParseState *pstate; + List *groupClause; + } transform_ungrouped_target_context; + + static void check_ungrouped_columns(Node *node, ParseState *pstate, List *groupClauses, bool have_non_var_grouping); static bool check_ungrouped_columns_walker(Node *node, check_ungrouped_columns_context *context); + static Node *transform_ungrouped_target(Node *node, ParseState *pstate, List *groupClauses); /* *************** *** 397,402 **** --- 405,422 ---- } } + /* check this state (for one set grouping sets), or parent state (for multiple grouping sets */ + if (pstate->p_hasGroupingSets || (pstate->parentParseState && pstate->parentParseState->p_hasGroupingSets)) + { + clause = (Node *) transform_ungrouped_target((Node *) qry->targetList, + pstate, + groupClauses); + /* HACK!!! - move to transform part*/ + qry->targetList = (List *) clause; + } + else + clause = (Node *) qry->targetList; + /* * Check the targetlist and HAVING clause for ungrouped variables. * *************** *** 405,411 **** * WINDOW clauses. For that matter, it's also going to examine the * grouping expressions themselves --- but they'll all pass the test ... */ - clause = (Node *) qry->targetList; if (hasJoinRTEs) clause = flatten_join_alias_vars(root, clause); check_ungrouped_columns(clause, pstate, --- 425,430 ---- *************** *** 741,743 **** --- 760,835 ---- args, COERCE_DONTCARE); } + + /* + * transform_ungrouped_cols_mutator - + * All non aggregates, all non constatnt columns are replaced by NULL, + * grouping and grouping_id functions are replaced by adequate constatnts. + */ + static Node * + transform_ungrouped_target_mutator(Node *node, + transform_ungrouped_target_context *context) + { + if (node == NULL) + return NULL; + if (IsA(node, Aggref)) + return node; + + if (IsA(node, GroupingSetsFunction)) + { + GroupingSetsFunction *gsf = (GroupingSetsFunction *) node; + int result = 0; + + if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING) + { + result = list_member(context->groupClause, gsf->expr) ? 0 : 1; + return (Node *) make_const(context->pstate, makeInteger(result), gsf->location); + } + else if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING_ID) + { + ListCell *el; + + foreach(el, gsf->expr_list) + { + result = result << 1; + if (!list_member(context->groupClause, lfirst(el))) + result = result | 0x01; + } + return (Node *) make_const(context->pstate, makeInteger(result), gsf->location); + } + } + + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (list_member(context->groupClause, node)) + return node; + else + return (Node *) makeNullConst(var->vartype, var->vartypmod); + } + else if (IsA(node, FuncExpr)) + { + FuncExpr *fexpr = (FuncExpr *) node; + + if (list_member(context->groupClause, node)) + return node; + else + return (Node *) makeNullConst(fexpr->funcresulttype, -1); + } + + return expression_tree_mutator(node, + transform_ungrouped_target_mutator, context); + } + + static Node * + transform_ungrouped_target(Node *node, ParseState *pstate, + List *groupClauses) + { + transform_ungrouped_target_context context; + + context.pstate = pstate; + context.groupClause = groupClauses; + + return transform_ungrouped_target_mutator(node, &context); + } *** ./src/backend/parser/parse_expr.c.orig 2010-07-06 21:18:57.000000000 +0200 --- ./src/backend/parser/parse_expr.c 2010-08-05 13:29:22.273031825 +0200 *************** *** 293,298 **** --- 293,357 ---- result = transformCurrentOfExpr(pstate, (CurrentOfExpr *) expr); break; + case T_GroupingSetsFunction: + { + GroupingSetsFunction *gsf = (GroupingSetsFunction *) expr; + + /* + * Because there are parser's conflict between a_expr rules and CUBE() + * and ROLLUP() operator, we have to do decision later by context. This + * transformation replace unevaluated gs operators by typical funccall. + * "cube" cannot be a reserved keyword. Contrib module contains a few + * functions "cube". + */ + if (gsf->kind == GROUPINGSETS_FUNCTION_CUBE || gsf->kind == GROUPINGSETS_FUNCTION_ROLLUP) + { + FuncCall *fc = makeNode(FuncCall); + + if (gsf->kind == GROUPINGSETS_FUNCTION_CUBE) + { + /* for cube() */ + fc->funcname = list_make1(makeString("cube")); + } + else + { + /* for rollup() */ + fc->funcname = list_make1(makeString("rollup")); + } + + fc->args = gsf->expr_list; + fc->agg_order = NIL; + fc->agg_star = false; + fc->agg_distinct = false; + fc->func_variadic = false; + fc->over = NULL; + fc->location = gsf->location; + result = transformFuncCall(pstate, (FuncCall *) fc); + } + else if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING) + { + /* this parser's node will be replaced later by NULL or constant */ + gsf->expr = transformExpr(pstate, gsf->expr); + result = (Node *) gsf; + } + else if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING_ID) + { + List *expr_list = NIL; + ListCell *lc; + + /* this parser's node will be replaced later by NULL or constant */ + foreach(lc, gsf->expr_list) + { + expr_list = lappend(expr_list, + transformExpr(pstate, + (Node *) lfirst(lc))); + } + gsf->expr_list = expr_list; + result = (Node *) gsf; + } + } + break; + /********************************************* * Quietly accept node types that may be presented when we are * called on an already-transformed tree. *************** *** 324,329 **** --- 383,389 ---- case T_CoerceToDomain: case T_CoerceToDomainValue: case T_SetToDefault: + case T_GroupingSetsEmptySet: { result = (Node *) expr; break; *** ./src/backend/parser/parse_gsets.c.orig 2010-08-03 14:24:18.042985208 +0200 --- ./src/backend/parser/parse_gsets.c 2010-08-05 15:09:44.722157284 +0200 *************** *** 0 **** --- 1,637 ---- + /*------------------------------------------------------------------------- + * + * parse_gsets.c + * parser grouping sets transformations + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL: $ + * + *------------------------------------------------------------------------- + */ + #include "postgres.h" + #include "catalog/namespace.h" + #include "nodes/nodes.h" + #include "nodes/makefuncs.h" + #include "nodes/nodeFuncs.h" + #include "nodes/value.h" + #include "nodes/pg_list.h" + #include "nodes/parsenodes.h" + #include "optimizer/tlist.h" + #include "parser/parse_clause.h" + #include "parser/parse_node.h" + #include "rewrite/rewriteManip.h" + + #include + + #define MAX_CUBE_FIELDS 8 + + #define is_empty_list(n) (n == NIL) + #define is_undef_loc(loc) (loc == -1) + #define IsGroupingSetsSpecification(n) (IsA(n, GroupingSetsSpecification)) + + typedef struct + { + ParseState *pstate; + List *groupClause; + bool isGlobalTL; + } transform_ungroup_cols_context; + + static List *add_distinct_set(List *gsets_list, List *gsets); + static List *add_distinct_sets(List *cum_gsets_list, List *gsets_list); + static Node *evalGroupByClause(List *element_list, bool has_empty_set); + static Node *evalGroupingSetsOp(ParseState *pstate, Node *node); + + /* + * return true, when GroupingSet is empty + */ + static bool + is_empty_grouping_set(Node *node) + { + if (node == NULL) + return true; + if (IsA(node, GroupingSetsEmptySet)) + return true; + if (IsGroupingSetsSpecification(node)) + return is_empty_list(((GroupingSetsSpecification *) node)->grouping_sets); + + return false; + } + + /* + * expandGroupingSets do: + * a) search end replace cube and rollup funccall nodes by GroupingSetsSpec + * nodes, + * b) multiply all set in GroupBy list, + * c) drop all None nodes. + * + */ + Node * + evalGroupingSets(ParseState *pstate, List *gsets_element_list) + { + ListCell *g; + List *grouping_sets = NIL; + bool has_empty_set = false; + int location = -1; /* undefined location now */ + bool use_grouping_sets = false; + + /* leave fast when grouplist is NIL */ + if (is_empty_list(gsets_element_list)) + return NULL; + + /* detect single group by grouping set */ + foreach(g, gsets_element_list) + { + Node *el = (Node *) lfirst(g); + Node *evaluated_element; + + /* + * Is it empty set. NULL is short cut for empty set. + * This has disadvantage - we lost location, but it is short. + */ + if (is_empty_grouping_set(el)) + { + has_empty_set = true; + continue; + } + + /* take location from first item of list */ + if (is_undef_loc(location)) + location = exprLocation(el); + + evaluated_element = evalGroupingSetsOp(pstate, el); + if (is_empty_grouping_set(evaluated_element)) + { + has_empty_set = true; + continue; + } + + if (IsA(el, GroupingSetsSpecification)) + use_grouping_sets = true; + else if (IsA(el, GroupingSetsFunction)) + { + switch (((GroupingSetsFunction *) el)->kind) + { + case GROUPINGSETS_FUNCTION_CUBE: + case GROUPINGSETS_FUNCTION_ROLLUP: + use_grouping_sets = true; + break; + default: + break; /* be compiler quiete */ + } + } + + grouping_sets = lappend(grouping_sets, evaluated_element); + } + + /* return original list of fields, when grouping sets are not used */ + if (!use_grouping_sets && !has_empty_set) + return (Node *) gsets_element_list; + + /* multiple all sets in GROUP BY list */ + pstate->p_hasGroupingSets = true; + + return (Node *) evalGroupByClause(grouping_sets, has_empty_set); + } + + /* + * This function has some purposes - first take list from row expression, + * second - remove all empty sets and remove all duplicate sets, + * ToDo: add flag to RowExpr that carry info about explicit ROW keyword + * because GROUPING SETS (a, (a,b)) is different from + * GROUPING SETS (a, ROW(a,b)) + */ + static List ** + prepareElements(List *elements, int *nfields, bool *has_empty_set) + { + ListCell *l; + List *felements = NIL; /* filtered elements, without empty sets */ + int _nfields; + List **result; + int i = 0; + + *has_empty_set = false; + foreach(l, elements) + { + if (is_empty_grouping_set((Node *) lfirst(l))) + { + *has_empty_set = true; + continue; + } + + felements = list_append_unique(felements, lfirst(l)); + } + + if (is_empty_list(felements)) + return NULL; + + _nfields = list_length(felements); + result = palloc(_nfields * sizeof(List *)); + + foreach(l, felements) + { + Node *el = (Node *) lfirst(l); + + switch (nodeTag(el)) + { + case T_RowExpr: + result[i++] = ((RowExpr *) el)->args; + break; + default: + result[i++] = list_make1(el); + + } + } + + *nfields = _nfields; + return result; + } + + /* + * This function add one grouping set to other. Result is list of distinct sets, + * etc list of distinct list. + * + * Note: the distinct in function name is not related to ALL|DISTINCT quantifier + * of GROUP BY clause + */ + static List * + add_distinct_set(List *gsets_list, List *gsets) + { + + List *uniqset = NIL; /* unique gsets */ + ListCell *l; + int act_len; + bool found = false; + + /* + * ensure so all fields in gsets_list are unique, + * all new items are unique there. + */ + foreach (l, gsets) + uniqset = list_append_unique(uniqset, lfirst(l)); + + /* same lists has same length */ + act_len = list_length(uniqset); + + foreach(l, gsets_list) + { + Node *node = (Node *) lfirst(l); + + Assert(IsA(node, List)); + if (list_length((List *) node) == act_len) + { + ListCell *lc; + + found = true; + foreach(lc, (List *) node) + if (!list_member(uniqset, (Node *) lfirst(lc))) + { + found = false; + break; + } + if (found) + break; + } + } + + if (!found) + return lappend(gsets_list, uniqset); + else + return gsets_list; + } + + /* + * This used in grouping sets(a, rollup(a,b)) + */ + static List * + add_distinct_sets(List *cum_gsets_list, List *gsets_list) + { + ListCell *l; + + foreach(l, gsets_list) + { + Node *el = (Node *) lfirst(l); + Assert(IsA(el, List)); + cum_gsets_list = add_distinct_set(cum_gsets_list, (List *) el); + } + return cum_gsets_list; + } + + /* + * Evaluate CUBE and ROLLUP operators + * replace all GroupingSetsFunction nodes (inside GROUP BY clause) by + * GroupingSetsSpecification nodes. + * + * This routine hasn't effect on GroupingSetsFunction nodes used in target list. These + * nodes are transformed to function calls - together with evaluation of Grouping, and + * Grouping_id functions under target list transformation. + */ + static Node * + evalGroupingSetsOp(ParseState *pstate, Node *node) + { + bool has_empty_set = false; + List *grouping_sets = NIL; + GroupingSetsSpecification *result; + int location = -1; /* be a compiler quiete */ + + /* leave when node is actually empty set */ + if (is_empty_grouping_set(node)) + return NULL; + + switch (nodeTag(node)) + { + case T_GroupingSetsFunction: + { + List **elements_vect; + + GroupingSetsFunction *gsf = (GroupingSetsFunction *) node; + int nfields = list_length(gsf->expr_list); + + if (gsf->kind == GROUPINGSETS_FUNCTION_CUBE && nfields > MAX_CUBE_FIELDS) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("too much parameters for CUBE operator"), + parser_errposition(pstate, gsf->location))); + + /* + * Because we will access to grouping set elements via index repeatly, then + * store pointers to elements to vector. prepareElements does little bit more + * it reduce duplicit elements and ensure so node type of every element will + * be a List (native list of one field's list) - de facto GroupingSets is + * list of lists. Leave when all elements are empty sets. + */ + elements_vect = prepareElements(gsf->expr_list, &nfields, &has_empty_set); + if (elements_vect == NULL) + return NULL; + + if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING || gsf->kind == GROUPINGSETS_FUNCTION_GROUPING_ID) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("function \"grouping\" or \"grouping_id\" is used in wrong context"), + parser_errposition(pstate, gsf->location))); + } + else if (gsf->kind == GROUPINGSETS_FUNCTION_CUBE) + { + /* + * Grouping sets CUBE operator evaluates parameters to set of parameter + * combinations like CUBE(a,b) -> {{a,b},{a},{b},{}}. The simple way to + * generate parameter combinations is using a bitmap shift: + * 111 7 {a,b,c}, 110 6 {a,b}, 101 5 {a,c}, 100 4 {a}, + * ... 001 1 c, 000 0 {} + * + * btab is array of initial values for n cycles iteration (based on + * number of unique grouping element) + */ + unsigned int btab[] = {0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; + unsigned int bitmap = btab[nfields - 1]; + unsigned int masc = bitmap; + unsigned int selector = 1 << (nfields - 1); + List *current_set; + int i; + + grouping_sets = NIL; + while (bitmap > 0) + { + unsigned int processed_bitmap = bitmap--; + + current_set = NIL; + for (i = 0; processed_bitmap > 0; i++) + { + if (processed_bitmap & selector) + current_set = list_union(current_set, elements_vect[i]); + processed_bitmap = (processed_bitmap << 1) & masc; + } + grouping_sets = lappend(grouping_sets, current_set); + + /* append a empty set */ + has_empty_set = true; + } + } + else if (gsf->kind == GROUPINGSETS_FUNCTION_ROLLUP) + { + /* + * Grouping Sets ROLLUP operator + * This operator evaluate set {a,b} to sets {{a,b},{a},{}} + */ + int i; + int j; + List *current_set; + + grouping_sets = NIL; + for (i = 0; i < nfields; i++) + { + current_set = NIL; + for (j = 0; j < nfields - i; j++) + current_set = list_union(current_set, elements_vect[j]); + + grouping_sets = lappend(grouping_sets, current_set); + } + + /* append a empty set */ + has_empty_set = true; + } + } + break; + case T_GroupingSetsSpecification: + { + /* + * Grouping Sets (xxxx) + * a) evaluates inner CUBE and ROLLUP operator + * b) drop duplicate sets + * c) detect inner empty sets + */ + ListCell *gse; + GroupingSetsSpecification *gss = (GroupingSetsSpecification *) node; + + grouping_sets = NIL; + location = gss->location; + + foreach(gse, gss->grouping_sets) + { + Node *grouping_element = (Node *) lfirst(gse); + Node *evaluated_element = evalGroupingSetsOp(pstate, grouping_element); + + if (is_empty_grouping_set(evaluated_element)) + { + has_empty_set = true; + continue; + } + + if (IsGroupingSetsSpecification(evaluated_element)) + { + GroupingSetsSpecification *egss = (GroupingSetsSpecification *) evaluated_element; + + if (egss->has_empty_set) + has_empty_set = true; + + if (egss->grouping_sets != NIL) + grouping_sets = add_distinct_sets(grouping_sets, egss->grouping_sets); + } + else if (IsA(evaluated_element, RowExpr)) + { + /* + * There are not possible to parse inner grouping sets + * directly (like grouping sets(a,b, >>(c,b)<< ) because + * there are a conflict with implicit row parse rule. So + * in this context we have to transform any RowExpr to + * grouping set element. + */ + ListCell *l; + RowExpr *row_expr = (RowExpr *) evaluated_element; + + foreach(l, row_expr->args) + { + if (is_empty_grouping_set((Node *) lfirst(l))) + { + has_empty_set = true; + break; + } + } + + grouping_sets = add_distinct_set(grouping_sets, row_expr->args); + } + else + grouping_sets = add_distinct_set(grouping_sets, list_make1(evaluated_element)); + } + } + break; + + default: + return node; + } + + if (grouping_sets == NIL) + return NULL; + + result = (GroupingSetsSpecification *) makeNode(GroupingSetsSpecification); + result->has_empty_set = has_empty_set; + result->grouping_sets = grouping_sets; + result->location = location; + + return (Node *) result; + } + + /* multiple two Grouping Sets elements + */ + static Node * + multiple_grouping_sets(Node *a, Node *b) + { + ListCell *l; + ListCell *lj; + List *grouping_sets = NIL; + GroupingSetsSpecification *result; + GroupingSetsSpecification *gss_a; + GroupingSetsSpecification *gss_b; + + /* leave when nodes are equal */ + if (equal(a, b)) + return a; + + if (!IsGroupingSetsSpecification(a) && !IsGroupingSetsSpecification(b)) + { + /* + * when both items are elements, then create new set as union + * a x b = (a,b) + */ + GroupingSetsSpecification *gss = (GroupingSetsSpecification *) makeNode(GroupingSetsSpecification); + gss->grouping_sets = list_make1(list_make2(a,b)); + gss->has_empty_set = false; + gss->location = exprLocation(a); + + return (Node *) gss; + } + if (IsGroupingSetsSpecification(a) && !IsGroupingSetsSpecification(b)) + { + Node *aux = a; + + /* when left operand is set and right not, then swap operands */ + a = b; b = aux; + } + + if (!IsGroupingSetsSpecification(a) && IsGroupingSetsSpecification(b)) + { + /* + * when left operand isn't set and right yes, copy a to every set in b + * a x (b,()) = ((a,b),(a)) + */ + gss_b = (GroupingSetsSpecification *) b; + + foreach(l, gss_b->grouping_sets) + { + List *s = (List *) lfirst(l); + + Assert(IsA(lfirst(l), List)); + + /* add item to all grouping sets */ + s = list_append_unique(s, a); + grouping_sets = add_distinct_set(grouping_sets, s); + } + + /* replace possible empty set in b by a */ + if (gss_b->has_empty_set) + { + grouping_sets = add_distinct_set(grouping_sets, (list_make1(a))); + gss_b->has_empty_set = false; + } + + gss_b->grouping_sets = grouping_sets; + + return (Node *) b; + } + + /* + * Both operand are grouping sets + * ((A,B),(C)) * ((X,Y),()) = ((A,B,X,Y),(A,B),(C,X,Y),(C)) + */ + Assert(IsGroupingSetsSpecification(a) && IsGroupingSetsSpecification(b)); + + gss_a = (GroupingSetsSpecification *) a; + gss_b = (GroupingSetsSpecification *) b; + + result = (GroupingSetsSpecification *) makeNode(GroupingSetsSpecification); + + foreach(l, gss_a->grouping_sets) + { + foreach(lj, gss_b->grouping_sets) + grouping_sets = add_distinct_set(grouping_sets, + list_union((List *) lfirst(l), + (List *) lfirst(lj))); + if (gss_b->has_empty_set) + grouping_sets = add_distinct_set(grouping_sets, + (List *) lfirst(l)); + } + + if (gss_a->has_empty_set) + foreach(l, gss_b->grouping_sets) + grouping_sets = add_distinct_set(grouping_sets, (List *) lfirst(l)); + + result->grouping_sets = grouping_sets; + /* when both operands has a empty set, then result will have a empty set too */ + result->has_empty_set = gss_a->has_empty_set && gss_b->has_empty_set; + result->location = gss_a->location; + + return (Node *) result; + } + + /* + * evaluate list of GROUP BY elements + * + * When any Grouping Sets is used, then multiply all parameters + * of GROUP BY A, B, C = ((A x B) x C) + */ + static Node * + evalGroupByClause(List *element_list, bool has_empty_set) + { + int nfields; + ListCell *lc; + bool initialized_stack = false; + Node *stack = NULL; + + /* fast leave when element_list is empty */ + if (has_empty_set) + { + if (is_empty_list(element_list)) + { + GroupingSetsSpecification *gss = makeNode(GroupingSetsSpecification); + gss->grouping_sets = NIL; + gss->has_empty_set = true; + gss->location = -1; + + return (Node *) gss; + } + } + + Assert(element_list != NIL); + nfields = list_length(element_list); + + foreach(lc, element_list) + { + Node *el = (Node *) lfirst(lc); + + /* fill arithmetic stack */ + if (!initialized_stack) + { + stack = el; + + /* + * a has_empty_set value goes from outside. Propagate it to stack, + * when it is true. When element isn't of GroupingSetsSpecification type, + * then cannot to carry value of has_empty_set, and then we have to wrap + * element to implicit GroupingSetsSpecification node. + */ + if (IsGroupingSetsSpecification(el)) + { + if (has_empty_set) + ((GroupingSetsSpecification *) stack)->has_empty_set = true; + } + else + { + if (has_empty_set) + { + GroupingSetsSpecification *gss = makeNode(GroupingSetsSpecification); + gss->grouping_sets = list_make1(list_make1(el)); + gss->has_empty_set = true; + gss->location = exprLocation(el); + stack = (Node *) gss; + } + } + + /* when there are only one parameter, leave function now */ + if (nfields == 1) + return stack; + + initialized_stack = true; + /* go to for next item */ + continue; + } + + Assert(stack != NULL); + stack = multiple_grouping_sets(stack, el); + } + + return stack; + } *** ./src/backend/parser/parse_target.c.orig 2010-02-26 03:00:52.000000000 +0100 --- ./src/backend/parser/parse_target.c 2010-08-05 13:18:04.358157078 +0200 *************** *** 1586,1591 **** --- 1586,1602 ---- case T_XmlSerialize: *name = "xmlserialize"; return 2; + case T_GroupingSetsFunction: + switch (((GroupingSetsFunction *) node)->kind) + { + case GROUPINGSETS_FUNCTION_GROUPING: + *name = "grouping"; + break; + case GROUPINGSETS_FUNCTION_GROUPING_ID: + *name = "grouping_id"; + break; + } + break; default: break; } *** ./src/include/nodes/nodes.h.orig 2010-07-12 19:01:06.000000000 +0200 --- ./src/include/nodes/nodes.h 2010-08-02 19:13:55.583786368 +0200 *************** *** 385,390 **** --- 385,394 ---- T_XmlSerialize, T_WithClause, T_CommonTableExpr, + T_GroupingSetsFunction, + T_GroupingSetsSpecification, + T_GroupingSetsEmptySet, + T_GroupBy, /* * TAGS FOR RANDOM OTHER STUFF *** ./src/include/nodes/parsenodes.h.orig 2010-07-26 01:21:22.000000000 +0200 --- ./src/include/nodes/parsenodes.h 2010-08-05 12:51:43.718032608 +0200 *************** *** 378,383 **** --- 378,435 ---- } SortBy; /* + * GroupBy - for GROUP BY [ GROUPING SETS ] clause + */ + typedef struct GroupBy + { + NodeTag type; + bool all; /* value of grouping sets quantifier - default is true */ + List *grouping_sets; /* list of grouping sets */ + int location; /* operator location, or -1 if none/unknown */ + } GroupBy; + + typedef enum GroupingSets_func_kind + { + GROUPINGSETS_FUNCTION_GROUPING, + GROUPINGSETS_FUNCTION_GROUPING_ID, + GROUPINGSETS_FUNCTION_CUBE, + GROUPINGSETS_FUNCTION_ROLLUP + } GroupingSets_func_kind; + + /* + * GroupingSetsFunction - for functions Grouping, Grouping_id and grouping + * sets operators Cube, Rollup. + */ + typedef struct GroupingSetsFunction + { + NodeTag type; + GroupingSets_func_kind kind; /* identify kind of grouping sets function or operator */ + Node *expr; /* a parameter for function Grouping */ + List *expr_list; /* parameters for Grouping_id and Cube and Rollup op */ + int location; /* token location, or -1 if unknown */ + } GroupingSetsFunction; + + /* + * GroupingSetsSpecification - for GROUPING SETS clause + */ + typedef struct GroupingSetsSpecification + { + NodeTag type; + List *grouping_sets; /* list of grouping sets */ + bool has_empty_set; /* true, when grouping sets contains any empty set */ + int location; /* token location, or -1 if unknown */ + } GroupingSetsSpecification; + + /* + * GroupingSetsEmptySet - for empty parentheses inside grouping sets specification + */ + typedef struct GroupingSetsEmptySet + { + NodeTag type; + int location; /* token location, or -1 if unknown */ + } GroupingSetsEmptySet; + + /* * WindowDef - raw representation of WINDOW and OVER clauses * * For entries in a WINDOW list, "name" is the window name being defined. *************** *** 956,962 **** List *targetList; /* the target list (of ResTarget) */ List *fromClause; /* the FROM clause */ Node *whereClause; /* WHERE qualification */ ! List *groupClause; /* GROUP BY clauses */ Node *havingClause; /* HAVING conditional-expression */ List *windowClause; /* WINDOW window_name AS (...), ... */ WithClause *withClause; /* WITH clause */ --- 1008,1014 ---- List *targetList; /* the target list (of ResTarget) */ List *fromClause; /* the FROM clause */ Node *whereClause; /* WHERE qualification */ ! Node *groupClause; /* GROUP BY clauses */ Node *havingClause; /* HAVING conditional-expression */ List *windowClause; /* WINDOW window_name AS (...), ... */ WithClause *withClause; /* WITH clause */ *** ./src/include/parser/kwlist.h.orig 2010-02-12 18:33:21.000000000 +0100 --- ./src/include/parser/kwlist.h 2010-08-02 15:00:33.799910696 +0200 *************** *** 99,104 **** --- 99,105 ---- PG_KEYWORD("createuser", CREATEUSER, UNRESERVED_KEYWORD) PG_KEYWORD("cross", CROSS, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("csv", CSV, UNRESERVED_KEYWORD) + PG_KEYWORD("cube", CUBE, RESERVED_KEYWORD) PG_KEYWORD("current", CURRENT_P, UNRESERVED_KEYWORD) PG_KEYWORD("current_catalog", CURRENT_CATALOG, RESERVED_KEYWORD) PG_KEYWORD("current_date", CURRENT_DATE, RESERVED_KEYWORD) *************** *** 171,176 **** --- 172,179 ---- PG_KEYWORD("granted", GRANTED, UNRESERVED_KEYWORD) PG_KEYWORD("greatest", GREATEST, COL_NAME_KEYWORD) PG_KEYWORD("group", GROUP_P, RESERVED_KEYWORD) + PG_KEYWORD("grouping", GROUPING, RESERVED_KEYWORD) + PG_KEYWORD("grouping_id", GROUPING_ID, COL_NAME_KEYWORD) PG_KEYWORD("handler", HANDLER, UNRESERVED_KEYWORD) PG_KEYWORD("having", HAVING, RESERVED_KEYWORD) PG_KEYWORD("header", HEADER_P, UNRESERVED_KEYWORD) *************** *** 318,323 **** --- 321,327 ---- PG_KEYWORD("right", RIGHT, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("role", ROLE, UNRESERVED_KEYWORD) PG_KEYWORD("rollback", ROLLBACK, UNRESERVED_KEYWORD) + PG_KEYWORD("rollup", ROLLUP, RESERVED_KEYWORD) PG_KEYWORD("row", ROW, COL_NAME_KEYWORD) PG_KEYWORD("rows", ROWS, UNRESERVED_KEYWORD) PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD) *************** *** 336,341 **** --- 340,346 ---- PG_KEYWORD("session_user", SESSION_USER, RESERVED_KEYWORD) PG_KEYWORD("set", SET, UNRESERVED_KEYWORD) PG_KEYWORD("setof", SETOF, COL_NAME_KEYWORD) + PG_KEYWORD("sets", SETS, UNRESERVED_KEYWORD) PG_KEYWORD("share", SHARE, UNRESERVED_KEYWORD) PG_KEYWORD("show", SHOW, UNRESERVED_KEYWORD) PG_KEYWORD("similar", SIMILAR, TYPE_FUNC_NAME_KEYWORD) *** ./src/include/parser/parse_node.h.orig 2010-02-26 03:01:26.000000000 +0100 --- ./src/include/parser/parse_node.h 2010-08-05 10:55:19.581032166 +0200 *************** *** 108,113 **** --- 108,114 ---- bool p_locked_from_parent; Relation p_target_relation; RangeTblEntry *p_target_rangetblentry; + bool p_hasGroupingSets; /* * Optional hook functions for parser callbacks. These are null unless *************** *** 149,152 **** --- 150,155 ---- Node *assignFrom); extern Const *make_const(ParseState *pstate, Value *value, int location); + extern Node *evalGroupingSets(ParseState *pstate, List *gsets_element_list); + #endif /* PARSE_NODE_H */