grouping sets - updated patch
Hello
I fixed an issues with empty sets. It just work, but there are some ugly hacks.
It's really needs own planner node - now grouping functions are not
supported by ORDER BY clause.
Regards
Pavel Stehule
Attachments:
verze003.difftext/x-patch; charset=US-ASCII; name=verze003.diffDownload
*** ./src/backend/nodes/copyfuncs.c.orig 2010-08-09 15:34:35.448223379 +0200
--- ./src/backend/nodes/copyfuncs.c 2010-08-09 15:35:01.905222827 +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-08-09 15:34:35.450099758 +0200
--- ./src/backend/nodes/equalfuncs.c 2010-08-09 15:35:01.908222800 +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/nodeFuncs.c.orig 2010-02-12 18:33:20.000000000 +0100
--- ./src/backend/nodes/nodeFuncs.c 2010-08-09 20:53:15.953222162 +0200
***************
*** 223,228 ****
--- 223,231 ----
case T_PlaceHolderVar:
type = exprType((Node *) ((PlaceHolderVar *) expr)->phexpr);
break;
+ case T_GroupingSetsFunction:
+ type = INT4OID;
+ break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
type = InvalidOid; /* keep compiler quiet */
***************
*** 1373,1378 ****
--- 1376,1401 ----
break;
case T_PlaceHolderInfo:
return walker(((PlaceHolderInfo *) node)->ph_var, context);
+ case T_GroupingSetsFunction:
+ {
+ GroupingSetsFunction *gsf = (GroupingSetsFunction *) node;
+
+ if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING)
+ {
+ if (expression_tree_walker((Node *) gsf->expr,
+ walker, context))
+ return true;
+ }
+ else if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING_ID)
+ {
+ if (expression_tree_walker((Node *) gsf->expr_list,
+ walker, context))
+ return true;
+ }
+ }
+ break;
+
+
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
***************
*** 2062,2068 ****
--- 2085,2108 ----
return (Node *) newnode;
}
break;
+ case T_GroupingSetsFunction:
+ {
+ GroupingSetsFunction *gsf = (GroupingSetsFunction *) node;
+ GroupingSetsFunction *newnode;
+
+ FLATCOPY(newnode, gsf, GroupingSetsFunction);
+
+ if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING)
+ MUTATE(newnode->expr, gsf->expr, Expr *);
+ else if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING_ID)
+ MUTATE(newnode->expr_list, gsf->expr_list, List *);
+
+ return (Node *) newnode;
+ }
+ break;
default:
+ Assert(false);
+ elog_node_display(ERROR, "wrong node", node, true);
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
break;
*** ./src/backend/nodes/outfuncs.c.orig 2010-08-09 15:34:35.451097798 +0200
--- ./src/backend/nodes/outfuncs.c 2010-08-09 15:35:01.915227556 +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-08-09 15:34:35.453099666 +0200
--- ./src/backend/nodes/readfuncs.c 2010-08-09 15:35:01.917222582 +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/optimizer/util/clauses.c.orig 2010-03-19 23:54:41.000000000 +0100
--- ./src/backend/optimizer/util/clauses.c 2010-08-09 22:39:23.144222535 +0200
***************
*** 2970,2975 ****
--- 2970,2995 ----
return eval_const_expressions_mutator((Node *) phv->phexpr,
context);
}
+ if (IsA(node, GroupingSetsFunction))
+ {
+ GroupingSetsFunction *gsf = (GroupingSetsFunction *) node;
+
+ if (gsf->kind == GROUPINGSETS_FUNCTION_GROUPING)
+ {
+ Datum result = Int32GetDatum(1);
+ int16 typLen;
+ bool typByVal;
+ Datum pval;
+
+ get_typlenbyval(INT4OID, &typLen, &typByVal);
+ return (Node *) makeConst(INT4OID,
+ -1,
+ (int) typLen,
+ result,
+ false,
+ typByVal);
+ }
+ }
/*
* For any node type not handled above, we recurse using
*** ./src/backend/parser/analyze.c.orig 2010-02-26 03:00:49.000000000 +0100
--- ./src/backend/parser/analyze.c 2010-08-09 22:38:52.369097509 +0200
***************
*** 29,34 ****
--- 29,35 ----
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/var.h"
+ #include "parser/parser.h"
#include "parser/analyze.h"
#include "parser/parse_agg.h"
#include "parser/parse_clause.h"
***************
*** 63,68 ****
--- 64,75 ----
ExplainStmt *stmt);
static void transformLockingClause(ParseState *pstate, Query *qry,
LockingClause *lc, bool pushedDown);
+ static SelectStmt *transformGroupingSets(ParseState *pstate, SelectStmt *stmt);
+
+ extern Node *transform_ungrouped_target(Node *node, ParseState *pstate, List *groupClauses);
+
+ Node *replace_groupingsets_functions_mutator(Node *node,
+ void *context);
/*
***************
*** 178,183 ****
--- 185,192 ----
case T_SelectStmt:
{
SelectStmt *n = (SelectStmt *) parseTree;
+
+ n = transformGroupingSets(pstate, n);
if (n->valuesLists)
result = transformValuesClause(pstate, n);
***************
*** 766,771 ****
--- 775,803 ----
return result;
}
+ Node *
+ replace_groupingsets_functions_mutator(Node *node,
+ void *context)
+ {
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, Query))
+ {
+ return (Node *)query_tree_mutator((Query*) node,
+ replace_groupingsets_functions_mutator,
+ (void *) context,
+ 0);
+ }
+
+ if (IsA(node, GroupingSetsFunction))
+ {
+ }
+
+ return expression_tree_mutator(node,
+ replace_groupingsets_functions_mutator,
+ (void *) context);
+ }
+
/*
* transformSelectStmt -
***************
*** 826,833 ****
true /* fix unknowns */ ,
false /* allow SQL92 rules */ );
qry->groupClause = transformGroupClause(pstate,
! stmt->groupClause,
&qry->targetList,
qry->sortClause,
false /* allow SQL92 rules */ );
--- 858,871 ----
true /* fix unknowns */ ,
false /* allow SQL92 rules */ );
+ /*
+ * In this moment grouping sets are transformed, so stmt->groupClause,
+ * have to be a list or NULL
+ */
+ Assert(stmt->groupClause == NULL || IsA(stmt->groupClause, List));
+
qry->groupClause = transformGroupClause(pstate,
! (List *) stmt->groupClause,
&qry->targetList,
qry->sortClause,
false /* allow SQL92 rules */ );
***************
*** 892,897 ****
--- 930,938 ----
(LockingClause *) lfirst(l), false);
}
+
+ qry = replace_groupingsets_functions_mutator(qry, qry->groupClause);
+
return qry;
}
***************
*** 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);
--- 965,971 ----
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);
***************
*** 2241,2243 ****
--- 2282,2492 ----
rc->pushedDown = pushedDown;
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);
+ }
+
+ /*
+ * used for empty grouping set to ensure GroupAggregate
+ */
+ static Node *
+ makeFakeGroupByClause(void)
+ {
+ A_Const *n = makeNode(A_Const);
+ TypeCast *t = makeNode(TypeCast);
+
+ n->val.type = T_String;
+ n->val.val.str = "t";
+ n->location = -1;
+
+ t->arg = (Node *) n;
+ t->typeName = SystemTypeName("bool");
+ t->location = -1;
+ return (Node *) list_make1(t);
+ }
+
+
+ /*
+ * 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)
+ {
+
+ /*
+ * there is big hack,only for this WIP version,
+ * Now we don't know if modified target list will contain
+ * a aggregate or not. But we have to ensure a aggregate
+ * mode - and then we use a simply trick, use a GROUP BY by "true"
+ */
+ SelectStmt *setop = makeSelectStmt(NIL, NIL);
+
+ rarg = makeSelectStmt(copyObject(stmt->targetList),
+ list_make1(makeRangeVar(NULL, "GroupingSets", -1)));
+ rarg->havingClause = copyObject(stmt->havingClause);
+ rarg->groupClause = makeFakeGroupByClause();
+
+ 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);
+
+ }
+ else
+ {
+ cteedstmt->groupClause = makeFakeGroupByClause();
+ }
+ }
+
+ ((SelectStmt *)cte->ctequery)->targetList = makeStarTargetList();
+ ((SelectStmt *)cte->ctequery)->groupClause = NULL;
+ ((SelectStmt *)cte->ctequery)->havingClause = 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-08-09 15:34:35.456099145 +0200
--- ./src/backend/parser/gram.y 2010-08-09 17:12:42.521222576 +0200
***************
*** 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
--- 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 TriggerFuncArgs select_limit
opt_select_limit opclass_item_list opclass_drop_list
opt_opfamily transaction_mode_list_or_empty
TableFuncElementList opt_type_modifiers
***************
*** 437,442 ****
--- 437,446 ----
opt_frame_clause frame_extent frame_bound
%type <str> opt_existing_window_name
+ %type <node> gsets_element gsets_empty_set gsets_specification group_clause
+ %type <list> gsets_element_list
+ %type <boolean> 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*/
! {
! $$ = NULL;
! }
! ;
!
! 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,9968 ----
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 Grouping sets operators, but
+ * it has a same syntax like function call, and therefore
+ * must be a processed here - other way means a parser's
+ * conflict.
+ */
+ 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 ****
--- 11132,11138 ----
| SERVER
| SESSION
| SET
+ | SETS
| SHARE
| SHOW
| SIMPLE
***************
*** 11120,11125 ****
--- 11210,11216 ----
| EXTRACT
| FLOAT_P
| GREATEST
+ | GROUPING_ID
| INOUT
| INT_P
| INTEGER
***************
*** 11214,11219 ****
--- 11305,11311 ----
| COLUMN
| CONSTRAINT
| CREATE
+ | CUBE
| CURRENT_CATALOG
| CURRENT_DATE
| CURRENT_ROLE
***************
*** 11235,11240 ****
--- 11327,11333 ----
| FROM
| GRANT
| GROUP_P
+ | GROUPING
| HAVING
| IN_P
| INITIALLY
***************
*** 11256,11261 ****
--- 11349,11355 ----
| PRIMARY
| REFERENCES
| RETURNING
+ | ROLLUP
| SELECT
| SESSION_USER
| SOME
*** ./src/backend/parser/Makefile.orig 2010-08-09 15:34:35.458097731 +0200
--- ./src/backend/parser/Makefile 2010-08-09 15:35:01.924224125 +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-08-09 15:34:35.459099259 +0200
--- ./src/backend/parser/parse_agg.c 2010-08-09 22:38:32.920097662 +0200
***************
*** 34,43 ****
--- 34,52 ----
int sublevels_up;
} check_ungrouped_columns_context;
+ typedef struct
+ {
+ ParseState *pstate;
+ List *groupClause;
+ bool isWhereClause;
+ } 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);
+ Node *transform_ungrouped_target(Node *node, ParseState *pstate, List *groupClauses, bool isWhereClause);
/*
***************
*** 397,402 ****
--- 406,423 ----
}
}
+ /* 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, false);
+ /* 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,
--- 426,431 ----
***************
*** 414,419 ****
--- 434,449 ----
clause = (Node *) qry->havingQual;
if (hasJoinRTEs)
clause = flatten_join_alias_vars(root, clause);
+
+ if (pstate->p_hasGroupingSets || (pstate->parentParseState && pstate->parentParseState->p_hasGroupingSets))
+ {
+
+ clause = (Node *) transform_ungrouped_target((Node *) clause,
+ pstate,
+ groupClauses, false);
+ qry->havingQual = clause;
+ }
+
check_ungrouped_columns(clause, pstate,
groupClauses, have_non_var_grouping);
***************
*** 741,743 ****
--- 771,852 ----
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 (context->isWhereClause)
+ ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("Grouping sets function cannot be used in this clause"),
+ parser_errposition(context->pstate, gsf->location)));
+
+ 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);
+ }
+
+ Node *
+ transform_ungrouped_target(Node *node, ParseState *pstate,
+ List *groupClauses, bool isWhereClause)
+ {
+ transform_ungrouped_target_context context;
+
+ context.pstate = pstate;
+ context.groupClause = groupClauses;
+ context.isWhereClause = isWhereClause;
+
+ return transform_ungrouped_target_mutator(node, &context);
+ }
*** ./src/backend/parser/parse_expr.c.orig 2010-08-09 15:34:35.461097776 +0200
--- ./src/backend/parser/parse_expr.c 2010-08-09 18:16:37.866097201 +0200
***************
*** 293,298 ****
--- 293,338 ----
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. But these operators cannot be used in expr.
+ * So raise syntax error here.
+ */
+ if (gsf->kind == GROUPINGSETS_FUNCTION_CUBE || gsf->kind == GROUPINGSETS_FUNCTION_ROLLUP)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("grouping sets operator \"cube\" or \"rollup\" cannot be used here"),
+ parser_errposition(pstate, gsf->location)));
+ }
+ 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 ****
--- 364,370 ----
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-09 15:34:48.079097706 +0200
--- ./src/backend/parser/parse_gsets.c 2010-08-09 15:35:01.927222352 +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 <string.h>
+
+ #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-08-09 15:34:35.482097728 +0200
--- ./src/backend/parser/parse_target.c 2010-08-09 17:10:04.418097472 +0200
***************
*** 1586,1591 ****
--- 1586,1604 ----
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;
+ default:
+ break; /* be a compiler quiet */
+ }
+ break;
default:
break;
}
*** ./src/include/nodes/nodes.h.orig 2010-08-09 15:34:35.483099256 +0200
--- ./src/include/nodes/nodes.h 2010-08-09 15:35:01.928222413 +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-08-09 15:34:35.485097772 +0200
--- ./src/include/nodes/parsenodes.h 2010-08-09 15:35:01.929222823 +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-08-09 15:34:35.486099719 +0200
--- ./src/include/parser/kwlist.h 2010-08-09 15:35:01.930224909 +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-08-09 15:34:35.487097545 +0200
--- ./src/include/parser/parse_node.h 2010-08-09 15:35:01.931222805 +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 */
On Mon, Aug 09, 2010 at 10:59:26PM +0200, Pavel Stehule wrote:
Hello
I fixed an issues with empty sets. It just work, but there are some ugly hacks.
It's really needs own planner node - now grouping functions are not
supported by ORDER BY clause.
I haven't made it through the last version much, but I'll poke through this
instead. I have a few days of family business coming up, and might be
unrespondive during that time.
--
Joshua Tolley / eggyknap
End Point Corporation
http://www.endpoint.com
2010/8/10 Joshua Tolley <eggyknap@gmail.com>:
On Mon, Aug 09, 2010 at 10:59:26PM +0200, Pavel Stehule wrote:
Hello
I fixed an issues with empty sets. It just work, but there are some ugly hacks.
It's really needs own planner node - now grouping functions are not
supported by ORDER BY clause.I haven't made it through the last version much, but I'll poke through this
instead. I have a few days of family business coming up, and might be
unrespondive during that time.
ok,
Pavel
Show quoted text
--
Joshua Tolley / eggyknap
End Point Corporation
http://www.endpoint.com-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)iEYEARECAAYFAkxgxlgACgkQRiRfCGf1UMM3NwCgkJ3EEWIj6MLiDcU2SHT/hH7a
4BsAn2hTqqzsLYLFZbflIJK/x/WMsZ2d
=dIod
-----END PGP SIGNATURE-----