On Mon, Aug 02, 2010 at 11:50:00PM -0600, Josh Tolley wrote:
> In case anyone's interested, I've taken the CTE-based grouping sets
> patch from [1] and made it apply to 9.1, attached. I haven't yet
> done things like checked it for whitespace consistency, style
> conformity, or anything else, but (tuits permitting) hope to figure
> out how it works and get it closer to commitability in some upcoming
> commitfest.
>
> I mention it here so that if someone else is working on it, we can
> avoid duplicated effort, and to see if a CTE-based grouping sets
> implementation is really the way we think we want to go.
>
> [1]
> http://archives.postgresql.org/pgsql-hackers/2009-05/msg00700.php
I've added back one file in the patch enclosed here. I'm still
getting compile fails from
CC="ccache gcc" ./configure --prefix=$PG_PREFIX --with-pgport=$PGPORT
--with-perl --with-libxml --enable-debug --enable-cassert
make
Log from that also enclosed.
Cheers,
David.
--
David Fetter <[email protected]> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: [email protected]
iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile
index a8f4c07..fb248a6 100644
--- a/src/backend/parser/Makefile
+++ b/src/backend/parser/Makefile
@@ -15,7 +15,7 @@ override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS= analyze.o gram.o keywords.o kwlookup.o parser.o \
parse_agg.o parse_clause.o parse_coerce.o parse_cte.o parse_expr.o \
parse_func.o parse_node.o parse_oper.o parse_param.o parse_relation.o \
- parse_target.o parse_type.o parse_utilcmd.o scansup.o
+ parse_target.o parse_type.o parse_utilcmd.o scansup.o parse_gsets.o
FLEXFLAGS = -CF
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 6b99a10..1b579a8 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -34,6 +34,7 @@
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
#include "parser/parse_cte.h"
+#include "parser/parse_gsets.h"
#include "parser/parse_oper.h"
#include "parser/parse_param.h"
#include "parser/parse_relation.h"
@@ -150,6 +151,163 @@ parse_sub_analyze(Node *parseTree, ParseState
*parentParseState,
}
/*
+ * process GROUPING SETS
+ */
+static SelectStmt *
+makeSelectStmt(List *targetList, List *fromClause)
+{
+ SelectStmt *n = makeNode(SelectStmt);
+ n->distinctClause = NULL;
+ n->intoClause = NULL;
+ n->targetList = targetList;
+ n->fromClause = fromClause;
+ n->whereClause = NULL;
+ n->groupClause = NULL;
+ n->havingClause = NULL;
+ n->windowClause = NIL;
+ n->withClause = NULL;
+ n->valuesLists = NIL;
+ n->sortClause = NIL;
+ n->limitOffset = NULL;
+ n->limitCount = NULL;
+ n->lockingClause = NIL;
+ n->op = SETOP_NONE;
+ n->all = false;
+ n->larg = NULL;
+ n->rarg = NULL;
+ return n;
+}
+
+static List *
+makeStarTargetList(void)
+{
+ ResTarget *rt = makeNode(ResTarget);
+
+ rt->name = NULL;
+ rt->indirection = NIL;
+ rt->val = (Node *) makeNode(ColumnRef);
+ ((ColumnRef *) rt->val)->fields = list_make1(makeNode(A_Star));
+ rt->location = -1;
+
+ return list_make1(rt);
+}
+
+static SelectStmt *
+transformGroupingSets(ParseState *pstate, SelectStmt *stmt)
+{
+ if (stmt->groupClause && IsA(stmt->groupClause, GroupByClause))
+ {
+ GroupingSetsSpec *gss = (GroupingSetsSpec *)
expandGroupingSets(pstate,
+ (List *)((GroupByClause
*)stmt->groupClause)->fields);
+
+ if (pstate->p_hasGroupingSets)
+ {
+ CommonTableExpr *cte = makeNode(CommonTableExpr);
+ SelectStmt *cteedstmt;
+ int ngroupingsets = list_length(gss->set_list) +
(gss->has_empty_set ? 1 : 0);
+ bool all = ((GroupByClause *)
stmt->groupClause)->all;
+
+ cteedstmt = makeSelectStmt(NIL, NIL);
+ cteedstmt->intoClause = stmt->intoClause;
+ cteedstmt->sortClause = stmt->sortClause;
+ cteedstmt->limitOffset = stmt->limitOffset;
+ cteedstmt->limitCount = stmt->limitCount;
+ cteedstmt->lockingClause = stmt->lockingClause;
+
+ cte->ctename = "**g**";
+ cte->ctequery = (Node *) stmt;
+ cte->location = -1;
+
+ cteedstmt->withClause = makeNode(WithClause);
+ cteedstmt->withClause->ctes = list_make1(cte);
+ cteedstmt->withClause->recursive = false;
+ cteedstmt->withClause->location = -1;
+
+ /* when is more than one grouping set, then we should
generate setop node */
+ if (ngroupingsets > 1)
+ {
+ /* add quuery under union all for every
grouping set */
+ SelectStmt *larg = NULL;
+ SelectStmt *rarg;
+ ListCell *lc;
+
+ foreach(lc, gss->set_list)
+ {
+ List *groupClause;
+
+ Assert(IsA(lfirst(lc), List));
+ groupClause = (List *) lfirst(lc);
+
+ if (larg == NULL)
+ {
+ larg =
makeSelectStmt(copyObject(stmt->targetList),
+
list_make1(makeRangeVar(NULL, "**g**", -1)));
+ larg->groupClause = (Node *)
groupClause;
+ larg->havingClause =
copyObject(stmt->havingClause);
+ }
+ else
+ {
+ SelectStmt *setop =
makeSelectStmt(NIL, NIL);
+
+ rarg =
makeSelectStmt(copyObject(stmt->targetList),
+
list_make1(makeRangeVar(NULL, "**g**", -1)));
+ rarg->groupClause = (Node *)
groupClause;
+ rarg->havingClause =
copyObject(stmt->havingClause);
+
+ setop->op = SETOP_UNION;
+ setop->larg = larg;
+ setop->rarg = rarg;
+ setop->all = all;
+
+ larg = setop;
+ }
+ }
+ if (gss->has_empty_set)
+ {
+ SelectStmt *setop =
makeSelectStmt(NIL, NIL);
+
+ rarg =
makeSelectStmt(copyObject(stmt->targetList),
+
list_make1(makeRangeVar(NULL, "**g**", -1)));
+ rarg->havingClause =
copyObject(stmt->havingClause);
+
+ setop->op = SETOP_UNION;
+ setop->larg = larg;
+ setop->rarg = rarg;
+ setop->all = all;
+
+ larg = setop;
+ }
+ /* merge larg to result */
+ cteedstmt->op = larg->op;
+ cteedstmt->larg = larg->larg;
+ cteedstmt->rarg = larg->rarg;
+ cteedstmt->all = larg->all;
+ }
+ else
+ {
+ /* there isn't used setop node */
+ cteedstmt->targetList =
copyObject(stmt->targetList);
+ cteedstmt->fromClause =
list_make1(makeRangeVar(NULL, "**g**", -1));
+ }
+
+ ((SelectStmt *)cte->ctequery)->targetList =
makeStarTargetList();
+ ((SelectStmt *)cte->ctequery)->groupClause = NULL;
+ ((SelectStmt *)cte->ctequery)->sortClause = NIL;
+ ((SelectStmt *)cte->ctequery)->limitOffset =
stmt->limitOffset;
+ ((SelectStmt *)cte->ctequery)->limitCount =
stmt->limitCount;
+ ((SelectStmt *)cte->ctequery)->lockingClause =
stmt->lockingClause;
+
+ return cteedstmt;
+ }
+ else
+ /* trim GroupByClause to groupByClause */
+ stmt->groupClause = (Node *)((GroupByClause
*)stmt->groupClause)->fields;
+ }
+
+ return stmt;
+}
+
+/*
* transformStmt -
* transform a Parse tree into a Query tree.
*/
@@ -179,6 +337,8 @@ transformStmt(ParseState *pstate, Node *parseTree)
{
SelectStmt *n = (SelectStmt *) parseTree;
+ n = transformGroupingSets(pstate, n);
+
if (n->valuesLists)
result = transformValuesClause(pstate,
n);
else if (n->op == SETOP_NONE)
@@ -827,7 +987,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
false /* allow SQL92 rules */ );
qry->groupClause = transformGroupClause(pstate,
-
stmt->groupClause,
+
(List *) stmt->groupClause,
&qry->targetList,
qry->sortClause,
false /* allow SQL92 rules */ );
@@ -924,7 +1084,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
Assert(stmt->targetList == NIL);
Assert(stmt->fromClause == NIL);
Assert(stmt->whereClause == NULL);
- Assert(stmt->groupClause == NIL);
+ Assert(stmt->groupClause == NULL);
Assert(stmt->havingClause == NULL);
Assert(stmt->windowClause == NIL);
Assert(stmt->op == SETOP_NONE);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1722036..0c9eff1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -111,6 +111,8 @@ static Node *makeBitStringConst(char *str, int location);
static Node *makeNullAConst(int location);
static Node *makeAConst(Value *v, int location);
static Node *makeBoolAConst(bool state, int location);
+static Node *makeGroupingSetsFunc(GroupingSetsFuncIdentity identity, Node
*expr,
+ List *expr_list, int
location);
static FuncCall *makeOverlaps(List *largs, List *rargs,
int location,
core_yyscan_t yyscanner);
static void check_qualified_name(List *names, core_yyscan_t yyscanner);
@@ -292,7 +294,7 @@ static TypeName *TableFuncTypeName(List *columns);
target_list insert_column_list set_target_list
set_clause_list set_clause multiple_set_clause
ctext_expr_list ctext_row def_list indirection
opt_indirection
- reloption_list group_clause TriggerFuncArgs
select_limit
+ reloption_list TriggerFuncArgs select_limit
opt_select_limit opclass_item_list
opclass_drop_list
opt_opfamily transaction_mode_list_or_empty
TableFuncElementList opt_type_modifiers
@@ -437,6 +439,11 @@ static TypeName *TableFuncTypeName(List *columns);
opt_frame_clause frame_extent frame_bound
%type <str> opt_existing_window_name
+%type <node> grouping_element empty_grouping_set grouping_sets_spec
+ group_clause
+%type <list> grouping_element_list
+%type <boolean> opt_grouping_set_quantifier
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -472,7 +479,7 @@ static TypeName *TableFuncTypeName(List *columns);
COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB
CREATEROLE CREATEUSER CROSS CSV CURRENT_P
- CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
+ CUBE CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
@@ -485,7 +492,7 @@ static TypeName *TableFuncTypeName(List *columns);
FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
FREEZE FROM FULL FUNCTION FUNCTIONS
- GLOBAL GRANT GRANTED GREATEST GROUP_P
+ GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING GROUPING_ID
HANDLER HAVING HEADER_P HOLD HOUR_P
@@ -519,10 +526,10 @@ static TypeName *TableFuncTypeName(List *columns);
RANGE READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REINDEX
RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
- RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
+ RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP ROW ROWS
RULE
SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE
SEQUENCES
- SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE
+ SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SETS SHARE
SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE_P START STATEMENT
STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P
SYMMETRIC SYSID SYSTEM_P
@@ -7797,9 +7804,55 @@ first_or_next: FIRST_P
{ $$ = 0; }
group_clause:
- GROUP_P BY expr_list
{ $$ = $3; }
- | /*EMPTY*/
{ $$ = NIL; }
- ;
+ GROUP_P BY opt_grouping_set_quantifier
grouping_element_list
+ {
+ GroupByClause *clause =
makeNode(GroupByClause);
+ clause->all = $3;
+ clause->fields = $4;
+ clause->location = @1;
+ $$ = (Node *) clause;
+ }
+ | /*EMPTY*/
+ {
+ $$ = NULL;
+ }
+ ;
+
+grouping_sets_spec:
+ GROUPING SETS '(' grouping_element_list ')'
+ {
+ /* We cannot identify and drop empty
sets yet. */
+ GroupingSetsSpec *gss =
makeNode(GroupingSetsSpec);
+ gss->set_list = $4;
+ gss->has_empty_set = false;
+ gss->location = @1;
+ $$ = (Node *) gss;
+ }
+ ;
+
+grouping_element_list:
+ grouping_element
{ $$ = list_make1($1); }
+ | grouping_element_list ',' grouping_element
{ $$ = lappend($1, $3); }
+ ;
+
+grouping_element:
+ a_expr
{ $$ = $1; }
+ | grouping_sets_spec
{ $$ = $1; }
+ | empty_grouping_set
{ $$ = $1; }
+ ;
+
+empty_grouping_set:
+ '(' ')'
+ {
+ $$ = makeNode(EmptySet);
+ }
+ ;
+
+opt_grouping_set_quantifier:
+ ALL { $$ = true; }
+ | DISTINCT { $$ = false; }
+ | /*EMPTY*/ { $$ = true; }
+ ;
having_clause:
HAVING a_expr
{ $$ = $2; }
@@ -9326,7 +9379,30 @@ c_expr: columnref
{ $$ = $1; }
r->location = @1;
$$ = (Node *)r;
}
- ;
+ | GROUPING '(' a_expr ')'
+ {
+ $$ =
makeGroupingSetsFunc(FUNCTION_GROUPING, $3, NIL, @1);
+ }
+ | GROUPING_ID '(' expr_list ')'
+ {
+ $$ =
makeGroupingSetsFunc(FUNCTION_GROUPING_ID, NULL, $3, @1);
+ }
+ | CUBE '(' expr_list ')'
+ {
+ /*
+ * Cube should be used in two different
contexts. First,
+ * as part of Grouping Sets
specification. Second, as
+ * normal UDF function from contrib
cube module. When isnot
+ * grouping sets context, then node is
transformated to
+ * FuncCall node later.
+ */
+ $$ =
makeGroupingSetsFunc(FUNCTION_CUBE, NULL, $3, @1);
+ }
+ | ROLLUP '(' expr_list ')'
+ {
+ $$ =
makeGroupingSetsFunc(FUNCTION_ROLLUP, NULL, $3, @1);
+ }
+ ;
/*
* func_expr is split out from c_expr just so that we have a classification
@@ -11043,6 +11119,7 @@ unreserved_keyword:
| SERVER
| SESSION
| SET
+ | SETS
| SHARE
| SHOW
| SIMPLE
@@ -11120,6 +11197,7 @@ col_name_keyword:
| EXTRACT
| FLOAT_P
| GREATEST
+ | GROUPING_ID
| INOUT
| INT_P
| INTEGER
@@ -11214,6 +11292,7 @@ reserved_keyword:
| COLUMN
| CONSTRAINT
| CREATE
+ | CUBE
| CURRENT_CATALOG
| CURRENT_DATE
| CURRENT_ROLE
@@ -11235,6 +11314,7 @@ reserved_keyword:
| FROM
| GRANT
| GROUP_P
+ | GROUPING
| HAVING
| IN_P
| INITIALLY
@@ -11256,6 +11336,7 @@ reserved_keyword:
| PRIMARY
| REFERENCES
| RETURNING
+ | ROLLUP
| SELECT
| SESSION_USER
| SOME
@@ -11761,6 +11842,18 @@ makeXmlExpr(XmlExprOp op, char *name, List
*named_args, List *args,
return (Node *) x;
}
+static Node *
+makeGroupingSetsFunc(GroupingSetsFuncIdentity identity, Node *expr, List
*expr_list, int location)
+{
+ GroupingSetsFunc *gsfunc = makeNode(GroupingSetsFunc);
+
+ gsfunc->identity = identity;
+ gsfunc->expr = expr;
+ gsfunc->expr_list = expr_list;
+ gsfunc->location = location;
+ return (Node *) gsfunc;
+}
+
/* parser_init()
* Initialize to parse one query string
*/
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 0a69bde..3aeea45 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -33,12 +33,20 @@ typedef struct
bool have_non_var_grouping;
int sublevels_up;
} check_ungrouped_columns_context;
+
+typedef struct
+{
+ ParseState *pstate;
+ List *groupClause;
+} transform_ungrouped_target_context;
static void check_ungrouped_columns(Node *node, ParseState *pstate,
List *groupClauses, bool
have_non_var_grouping);
static bool check_ungrouped_columns_walker(Node *node,
check_ungrouped_columns_context *context);
+static Node * transform_ungrouped_target(Node *node, ParseState *pstate,
+ List *groupClauses);
/*
* transformAggregateCall -
@@ -405,7 +413,16 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
* WINDOW clauses. For that matter, it's also going to examine the
* grouping expressions themselves --- but they'll all pass the test ...
*/
- clause = (Node *) qry->targetList;
+ if (pstate->parentParseState &&
pstate->parentParseState->p_hasGroupingSets)
+ {
+ clause = (Node *) transform_ungrouped_target((Node *)
qry->targetList,
+
pstate,
+
groupClauses);
+ /* HACK!!! - move to transform part*/
+ qry->targetList = clause;
+ }
+ else
+ clause = (Node *) qry->targetList;
if (hasJoinRTEs)
clause = flatten_join_alias_vars(root, clause);
check_ungrouped_columns(clause, pstate,
@@ -514,6 +531,83 @@ parseCheckWindowFuncs(ParseState *pstate, Query *qry)
}
/*
+ * transform_ungrouped_cols_mutator -
+ * All non aggregates non constatnt columns are replaced by NULL,
+ * grouping and grouping_id functions are replaced by constatnts.
+ */
+static Node *
+transform_ungrouped_target_mutator(Node *node,
+ transform_ungrouped_target_context
*context)
+{
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, Aggref))
+ return node;
+
+ if (IsA(node, GroupingSetsFunc))
+ {
+ GroupingSetsFunc *gsf = (GroupingSetsFunc *) node;
+ int result = 0;
+
+ if (gsf->identity == FUNCTION_GROUPING)
+ {
+ result = list_member(context->groupClause, gsf->expr) ?
0 : 1;
+ return (Node *) make_const(context->pstate,
makeInteger(result), gsf->location);
+ }
+ else if (gsf->identity == FUNCTION_GROUPING_ID)
+ {
+ ListCell *el;
+
+ foreach(el, gsf->expr_list)
+ {
+ result = result << 1;
+ if (!list_member(context->groupClause,
lfirst(el)))
+ result = result | 0x01;
+ }
+ return (Node *) make_const(context->pstate,
makeInteger(result), gsf->location);
+ }
+ else /* replace Cube and Rollup by FuncCall node */
+ {
+ /* ToDo: Support cube function */
+ }
+ }
+
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+
+ if (list_member(context->groupClause, node))
+ return node;
+ else
+ return (Node *) makeNullConst(var->vartype,
var->vartypmod);
+ }
+ else if (IsA(node, FuncExpr))
+ {
+ FuncExpr *fexpr = (FuncExpr *) node;
+
+ if (list_member(context->groupClause, node))
+ return node;
+ else
+ return (Node *) makeNullConst(fexpr->funcresulttype,
-1);
+ }
+
+ return expression_tree_mutator(node,
+ transform_ungrouped_target_mutator,
context);
+}
+
+static Node *
+transform_ungrouped_target(Node *node, ParseState *pstate,
+ List *groupClauses)
+{
+ transform_ungrouped_target_context context;
+
+ context.pstate = pstate;
+ context.groupClause = groupClauses;
+
+ return transform_ungrouped_target_mutator(node, &context);
+}
+
+/*
* check_ungrouped_columns -
* Scan the given expression tree for ungrouped variables (variables
* that are not listed in the groupClauses list and are not within
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 5e60374..609be1d 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -284,6 +284,25 @@ transformExpr(ParseState *pstate, Node *expr)
case T_BooleanTest:
result = transformBooleanTest(pstate, (BooleanTest *)
expr);
break;
+
+ case T_GroupingSetsFunc:
+ {
+ GroupingSetsFunc *gsf = (GroupingSetsFunc *)
expr;
+ ListCell *lc;
+ List *expr_list = NIL;
+
+ gsf->expr = (Node *) transformExpr(pstate,
(Node *) gsf->expr);
+
+ foreach(lc, gsf->expr_list)
+ {
+ expr_list = lappend(expr_list,
transformExpr(pstate,
+
(Node *) lfirst(lc)));
+ }
+ gsf->expr_list = expr_list;
+ result = expr;
+ break;
+ }
+
case T_CurrentOfExpr:
result = transformCurrentOfExpr(pstate, (CurrentOfExpr
*) expr);
@@ -320,6 +339,7 @@ transformExpr(ParseState *pstate, Node *expr)
case T_CoerceToDomain:
case T_CoerceToDomainValue:
case T_SetToDefault:
+ case T_EmptySet:
{
result = (Node *) expr;
break;
diff --git a/src/backend/parser/parse_target.c
b/src/backend/parser/parse_target.c
index e542dc0..1a3e969 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1586,6 +1586,22 @@ FigureColnameInternal(Node *node, char **name)
case T_XmlSerialize:
*name = "xmlserialize";
return 2;
+ case T_GroupingSetsFunc:
+ switch (((GroupingSetsFunc *) node)->identity)
+ {
+ case FUNCTION_GROUPING:
+ *name = "grouping";
+ return 2;
+ case FUNCTION_GROUPING_ID:
+ *name = "grouping_id";
+ return 2;
+ case FUNCTION_CUBE: /* by compiler
quite */
+ case FUNCTION_ROLLUP:
+ /* nothing */
+ break;
+ }
+ break;
+
default:
break;
}
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index a5f5df5..836e38d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -385,6 +385,10 @@ typedef enum NodeTag
T_XmlSerialize,
T_WithClause,
T_CommonTableExpr,
+ T_GroupingSetsFunc,
+ T_GroupingSetsSpec,
+ T_EmptySet,
+ T_GroupByClause,
/*
* TAGS FOR RANDOM OTHER STUFF
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index fec8d3c..904cb4a 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -378,6 +378,33 @@ typedef struct SortBy
} SortBy;
/*
+ * GroupingSetsSpec - for GROUP BY GROUPING SETS clause
+ */
+typedef struct GroupingSetsSpec
+{
+ NodeTag type;
+ List *set_list;
+ bool has_empty_set; /* true when grouping sets contains
empty set */
+ int location;
+} GroupingSetsSpec;
+
+typedef struct EmptySet
+{
+ NodeTag type;
+} EmptySet;
+
+/*
+ * GroupByClause for GROUP BY clause
+ */
+typedef struct GroupByClause
+{
+ NodeTag type;
+ bool all;
+ List *fields;
+ int location;
+} GroupByClause;
+
+/*
* WindowDef - raw representation of WINDOW and OVER clauses
*
* For entries in a WINDOW list, "name" is the window name being defined.
@@ -431,6 +458,26 @@ typedef struct WindowDef
FRAMEOPTION_END_CURRENT_ROW)
/*
+ * GroupingSetsFunc - parser node for Grouping, Grouping_id, Cube and Rollup
quasy functions
+ */
+typedef enum GroupingSetsFuncIdentity
+{
+ FUNCTION_GROUPING,
+ FUNCTION_GROUPING_ID,
+ FUNCTION_CUBE,
+ FUNCTION_ROLLUP
+} GroupingSetsFuncIdentity;
+
+typedef struct GroupingSetsFunc
+{
+ NodeTag type;
+ GroupingSetsFuncIdentity identity;
+ Node *expr;
+ List *expr_list;
+ int location;
+} GroupingSetsFunc;
+
+/*
* RangeSubselect - subquery appearing in a FROM clause
*/
typedef struct RangeSubselect
@@ -956,7 +1003,7 @@ typedef struct SelectStmt
List *targetList; /* the target list (of ResTarget) */
List *fromClause; /* the FROM clause */
Node *whereClause; /* WHERE qualification */
- List *groupClause; /* GROUP BY clauses */
+ Node *groupClause; /* GROUP BY clauses */
Node *havingClause; /* HAVING conditional-expression */
List *windowClause; /* WINDOW window_name AS (...), ... */
WithClause *withClause; /* WITH clause */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 49d4b6c..d0dcfe7 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -99,6 +99,7 @@ PG_KEYWORD("createrole", CREATEROLE, UNRESERVED_KEYWORD)
PG_KEYWORD("createuser", CREATEUSER, UNRESERVED_KEYWORD)
PG_KEYWORD("cross", CROSS, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("csv", CSV, UNRESERVED_KEYWORD)
+PG_KEYWORD("cube", CUBE, RESERVED_KEYWORD)
PG_KEYWORD("current", CURRENT_P, UNRESERVED_KEYWORD)
PG_KEYWORD("current_catalog", CURRENT_CATALOG, RESERVED_KEYWORD)
PG_KEYWORD("current_date", CURRENT_DATE, RESERVED_KEYWORD)
@@ -171,6 +172,8 @@ PG_KEYWORD("grant", GRANT, RESERVED_KEYWORD)
PG_KEYWORD("granted", GRANTED, UNRESERVED_KEYWORD)
PG_KEYWORD("greatest", GREATEST, COL_NAME_KEYWORD)
PG_KEYWORD("group", GROUP_P, RESERVED_KEYWORD)
+PG_KEYWORD("grouping", GROUPING, RESERVED_KEYWORD)
+PG_KEYWORD("grouping_id", GROUPING_ID, COL_NAME_KEYWORD)
PG_KEYWORD("handler", HANDLER, UNRESERVED_KEYWORD)
PG_KEYWORD("having", HAVING, RESERVED_KEYWORD)
PG_KEYWORD("header", HEADER_P, UNRESERVED_KEYWORD)
@@ -318,6 +321,7 @@ PG_KEYWORD("revoke", REVOKE, UNRESERVED_KEYWORD)
PG_KEYWORD("right", RIGHT, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("role", ROLE, UNRESERVED_KEYWORD)
PG_KEYWORD("rollback", ROLLBACK, UNRESERVED_KEYWORD)
+PG_KEYWORD("rollup", ROLLUP, RESERVED_KEYWORD)
PG_KEYWORD("row", ROW, COL_NAME_KEYWORD)
PG_KEYWORD("rows", ROWS, UNRESERVED_KEYWORD)
PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD)
@@ -336,6 +340,7 @@ PG_KEYWORD("session", SESSION, UNRESERVED_KEYWORD)
PG_KEYWORD("session_user", SESSION_USER, RESERVED_KEYWORD)
PG_KEYWORD("set", SET, UNRESERVED_KEYWORD)
PG_KEYWORD("setof", SETOF, COL_NAME_KEYWORD)
+PG_KEYWORD("sets", SETS, UNRESERVED_KEYWORD)
PG_KEYWORD("share", SHARE, UNRESERVED_KEYWORD)
PG_KEYWORD("show", SHOW, UNRESERVED_KEYWORD)
PG_KEYWORD("similar", SIMILAR, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_gsets.h b/src/include/parser/parse_gsets.h
new file mode 100644
index 0000000..98a9161
--- /dev/null
+++ b/src/include/parser/parse_gsets.h
@@ -0,0 +1,12 @@
+#include "parser/parse_node.h"
+#include "nodes/pg_list.h"
+
+#ifndef PARSE_GETS_H
+#define PARSE_GETS_H
+
+Node *expandGroupingSets(ParseState *pstate, List *grouplist);
+List *transformGroupingSetsSpec(ParseState *pstate, List *groupClause,
+ List
**GroupClauses, List **targetLists,
+ List
**targetList, List *sortClause);
+
+#endif /* PARSE_GETS_H */
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 1c1383b..f2b80bd 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -107,6 +107,7 @@ struct ParseState
bool p_is_update;
bool p_locked_from_parent;
Relation p_target_relation;
+ bool p_hasGroupingSets;
RangeTblEntry *p_target_rangetblentry;
/*
make -C src all
make[1]: Entering directory `/home/shackle/pggit/postgresql/src'
make -C port all
make[2]: Entering directory `/home/shackle/pggit/postgresql/src/port'
echo "#define PGBINDIR \"/home/shackle/tip/bin\"" >pg_config_paths.h
echo "#define PGSHAREDIR \"/home/shackle/tip/share/postgresql\""
>>pg_config_paths.h
echo "#define SYSCONFDIR \"/home/shackle/tip/etc/postgresql\""
>>pg_config_paths.h
echo "#define INCLUDEDIR \"/home/shackle/tip/include\"" >>pg_config_paths.h
echo "#define PKGINCLUDEDIR \"/home/shackle/tip/include/postgresql\""
>>pg_config_paths.h
echo "#define INCLUDEDIRSERVER \"/home/shackle/tip/include/postgresql/server\""
>>pg_config_paths.h
echo "#define LIBDIR \"/home/shackle/tip/lib\"" >>pg_config_paths.h
echo "#define PKGLIBDIR \"/home/shackle/tip/lib/postgresql\""
>>pg_config_paths.h
echo "#define LOCALEDIR \"/home/shackle/tip/share/locale\"" >>pg_config_paths.h
echo "#define DOCDIR \"/home/shackle/tip/share/doc//postgresql\""
>>pg_config_paths.h
echo "#define HTMLDIR \"/home/shackle/tip/share/doc//postgresql\""
>>pg_config_paths.h
echo "#define MANDIR \"/home/shackle/tip/share/man\"" >>pg_config_paths.h
ccache gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith
-Wdeclaration-after-statement -Wendif-labels -fno-strict-aliasing -fwrapv -g
-I../../src/port -DFRONTEND -I../../src/include -D_GNU_SOURCE
-I/usr/include/libxml2 -c -o path.o path.c
ar crs libpgport.a strlcat.o strlcpy.o chklocale.o dirmod.o exec.o noblock.o
path.o pgsleep.o pgstrcasecmp.o qsort.o qsort_arg.o sprompt.o thread.o
ccache gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith
-Wdeclaration-after-statement -Wendif-labels -fno-strict-aliasing -fwrapv -g
-I../../src/port -I../../src/include -D_GNU_SOURCE -I/usr/include/libxml2 -c
path.c -o path_srv.o
ar crs libpgport_srv.a strlcat_srv.o strlcpy_srv.o chklocale_srv.o dirmod_srv.o
exec_srv.o noblock_srv.o path_srv.o pgsleep_srv.o pgstrcasecmp_srv.o
qsort_srv.o qsort_arg_srv.o sprompt_srv.o thread_srv.o
make[2]: Leaving directory `/home/shackle/pggit/postgresql/src/port'
make -C timezone all
make[2]: Entering directory `/home/shackle/pggit/postgresql/src/timezone'
make -C ../../src/port all
make[3]: Entering directory `/home/shackle/pggit/postgresql/src/port'
make[3]: Nothing to be done for `all'.
make[3]: Leaving directory `/home/shackle/pggit/postgresql/src/port'
make[2]: Leaving directory `/home/shackle/pggit/postgresql/src/timezone'
make -C backend all
make[2]: Entering directory `/home/shackle/pggit/postgresql/src/backend'
make -C ../../src/port all
make[3]: Entering directory `/home/shackle/pggit/postgresql/src/port'
make[3]: Nothing to be done for `all'.
make[3]: Leaving directory `/home/shackle/pggit/postgresql/src/port'
make -C catalog schemapg.h
make[3]: Entering directory `/home/shackle/pggit/postgresql/src/backend/catalog'
make[3]: `schemapg.h' is up to date.
make[3]: Leaving directory `/home/shackle/pggit/postgresql/src/backend/catalog'
make -C parser gram.h
make[3]: Entering directory `/home/shackle/pggit/postgresql/src/backend/parser'
/usr/bin/bison -d -o gram.c gram.y
make[3]: Leaving directory `/home/shackle/pggit/postgresql/src/backend/parser'
prereqdir=`cd parser/ >/dev/null && pwd` && \
cd ../../src/include/parser/ && rm -f gram.h && \
ln -s "$prereqdir/gram.h" .
make -C access all
make[3]: Entering directory `/home/shackle/pggit/postgresql/src/backend/access'
make -C common all
make[4]: Entering directory
`/home/shackle/pggit/postgresql/src/backend/access/common'
make[4]: Nothing to be done for `all'.
make[4]: Leaving directory
`/home/shackle/pggit/postgresql/src/backend/access/common'
make -C gist all
make[4]: Entering directory
`/home/shackle/pggit/postgresql/src/backend/access/gist'
make[4]: Nothing to be done for `all'.
make[4]: Leaving directory
`/home/shackle/pggit/postgresql/src/backend/access/gist'
make -C hash all
make[4]: Entering directory
`/home/shackle/pggit/postgresql/src/backend/access/hash'
make[4]: Nothing to be done for `all'.
make[4]: Leaving directory
`/home/shackle/pggit/postgresql/src/backend/access/hash'
make -C heap all
make[4]: Entering directory
`/home/shackle/pggit/postgresql/src/backend/access/heap'
make[4]: Nothing to be done for `all'.
make[4]: Leaving directory
`/home/shackle/pggit/postgresql/src/backend/access/heap'
make -C index all
make[4]: Entering directory
`/home/shackle/pggit/postgresql/src/backend/access/index'
make[4]: Nothing to be done for `all'.
make[4]: Leaving directory
`/home/shackle/pggit/postgresql/src/backend/access/index'
make -C nbtree all
make[4]: Entering directory
`/home/shackle/pggit/postgresql/src/backend/access/nbtree'
make[4]: Nothing to be done for `all'.
make[4]: Leaving directory
`/home/shackle/pggit/postgresql/src/backend/access/nbtree'
make -C transam all
make[4]: Entering directory
`/home/shackle/pggit/postgresql/src/backend/access/transam'
make[4]: Nothing to be done for `all'.
make[4]: Leaving directory
`/home/shackle/pggit/postgresql/src/backend/access/transam'
make -C gin all
make[4]: Entering directory
`/home/shackle/pggit/postgresql/src/backend/access/gin'
make[4]: Nothing to be done for `all'.
make[4]: Leaving directory
`/home/shackle/pggit/postgresql/src/backend/access/gin'
make[3]: Leaving directory `/home/shackle/pggit/postgresql/src/backend/access'
make -C bootstrap all
make[3]: Entering directory
`/home/shackle/pggit/postgresql/src/backend/bootstrap'
make[3]: Nothing to be done for `all'.
make[3]: Leaving directory
`/home/shackle/pggit/postgresql/src/backend/bootstrap'
make -C catalog all
make[3]: Entering directory `/home/shackle/pggit/postgresql/src/backend/catalog'
make[3]: Nothing to be done for `all'.
make[3]: Leaving directory `/home/shackle/pggit/postgresql/src/backend/catalog'
make -C parser all
make[3]: Entering directory `/home/shackle/pggit/postgresql/src/backend/parser'
ccache gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith
-Wdeclaration-after-statement -Wendif-labels -fno-strict-aliasing -fwrapv -g
-I. -I. -I../../../src/include -D_GNU_SOURCE -I/usr/include/libxml2 -c -o
analyze.o analyze.c
ccache gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith
-Wdeclaration-after-statement -Wendif-labels -fno-strict-aliasing -fwrapv -g
-Wno-error -I. -I. -I../../../src/include -D_GNU_SOURCE -I/usr/include/libxml2
-c -o gram.o gram.c
gram.y: In function ‘base_yyparse’:
gram.y:7847: warning: assignment from incompatible pointer type
In file included from gram.y:11920:
scan.c: In function ‘yy_try_NUL_trans’:
scan.c:16246: warning: unused variable ‘yyg’
ccache gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith
-Wdeclaration-after-statement -Wendif-labels -fno-strict-aliasing -fwrapv -g
-I. -I. -I../../../src/include -D_GNU_SOURCE -I/usr/include/libxml2 -c -o
keywords.o keywords.c
ccache gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith
-Wdeclaration-after-statement -Wendif-labels -fno-strict-aliasing -fwrapv -g
-I. -I. -I../../../src/include -D_GNU_SOURCE -I/usr/include/libxml2 -c -o
parser.o parser.c
ccache gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith
-Wdeclaration-after-statement -Wendif-labels -fno-strict-aliasing -fwrapv -g
-I. -I. -I../../../src/include -D_GNU_SOURCE -I/usr/include/libxml2 -c -o
parse_agg.o parse_agg.c
parse_agg.c: In function ‘parseCheckAggregates’:
parse_agg.c:422: warning: assignment from incompatible pointer type
ccache gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith
-Wdeclaration-after-statement -Wendif-labels -fno-strict-aliasing -fwrapv -g
-I. -I. -I../../../src/include -D_GNU_SOURCE -I/usr/include/libxml2 -c -o
parse_expr.o parse_expr.c
ccache gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith
-Wdeclaration-after-statement -Wendif-labels -fno-strict-aliasing -fwrapv -g
-I. -I. -I../../../src/include -D_GNU_SOURCE -I/usr/include/libxml2 -c -o
parse_target.o parse_target.c
make[3]: *** No rule to make target `parse_gsets.o', needed by `objfiles.txt'.
Stop.
make[3]: Leaving directory `/home/shackle/pggit/postgresql/src/backend/parser'
make[2]: *** [parser-recursive] Error 2
make[2]: Leaving directory `/home/shackle/pggit/postgresql/src/backend'
make[1]: *** [all] Error 2
make[1]: Leaving directory `/home/shackle/pggit/postgresql/src'
make: *** [all] Error 2
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers