1) Queries using the having clause on base tables should work well

now. Here some tested features, (examples included in the patch):

1.1) Subselects in the having clause 1.2) Double nested subselects
1.3) Subselects used in the where clause and in the having clause
     simultaneously 1.4) Union Selects using having 1.5) Indexes
on the base relations are used correctly 1.6) Unallowed Queries
are prevented (e.g. qualifications in the
     having clause that belong to the where clause) 1.7) Insert
into as select

2) Queries using the having clause on view relations also work
   but there are some restrictions:

2.1) Create View as Select ... Having ...; using base tables in
the select 2.1.1) The Query rewrite system:

2.1.2) Why are only simple queries allowed against a view from 2.1)
? 2.2) Select ... from testview1, testview2, ... having...; 3) Bug
in ExecMergeJoin ??


Regards Stefan
This commit is contained in:
Bruce Momjian 1998-07-19 05:49:26 +00:00
parent 916710fc91
commit 460b20a43f
13 changed files with 997 additions and 537 deletions

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.22 1998/06/15 19:28:17 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.23 1998/07/19 05:49:12 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -222,6 +222,9 @@ UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
OffsetVarNodes((Node *) viewParse->targetList, 2);
OffsetVarNodes(viewParse->qual, 2);
OffsetVarNodes(viewParse->havingQual, 2);
/*
* find the old range table...
*/

View File

@ -109,22 +109,24 @@ ExecAgg(Agg *node)
bool isNull = FALSE,
isNull1 = FALSE,
isNull2 = FALSE;
do {
bool qual_result;
/* ---------------------
* get state info from node
* ---------------------
*/
/* We loop retrieving groups until we find one matching node->plan.qual */
do {
aggstate = node->aggstate;
if (aggstate->agg_done)
return NULL;
estate = node->plan.state;
econtext = aggstate->csstate.cstate.cs_ExprContext;
nagg = length(node->aggs);
aggregates = (Aggreg **) palloc(sizeof(Aggreg *) * nagg);
@ -235,8 +237,7 @@ ExecAgg(Agg *node)
}
}
}
/* ----------------
* for each tuple from the the outer plan, apply all the aggregates
* ----------------
@ -474,11 +475,6 @@ ExecAgg(Agg *node)
* slot and return it.
* ----------------
*/
}
while((ExecQual(fix_opids(node->plan.qual),econtext)!=true) &&
(node->plan.qual!=NULL));
ExecStoreTuple(oneTuple,
aggstate->csstate.css_ScanTupleSlot,
@ -488,8 +484,13 @@ ExecAgg(Agg *node)
resultSlot = ExecProject(projInfo, &isDone);
/* As long as the retrieved group does not match the qualifications it is ignored and
* the next group is fetched */
qual_result=ExecQual(fix_opids(node->plan.qual),econtext);
if (oneTuple)
pfree(oneTuple);
pfree(oneTuple);
}
while((node->plan.qual!=NULL) && (qual_result!=true));
return resultSlot;
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.16 1998/06/15 19:28:22 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.17 1998/07/19 05:49:13 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -365,7 +365,9 @@ CleanUpSort(Plan *plan)
{
Sort *sort = (Sort *) plan;
psort_end(sort);
/* This may need to be fixed or moved somewhere else, bjm */
/* psort_end(sort); */
}
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.27 1998/04/15 15:29:41 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.28 1998/07/19 05:49:14 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -95,6 +95,11 @@ Plan *
union_planner(Query *parse)
{
List *tlist = parse->targetList;
/* copy the original tlist, we will need the original one
* for the AGG node later on */
List *new_tlist = new_unsorted_tlist(tlist);
List *rangetable = parse->rtable;
Plan *result_plan = (Plan *) NULL;
@ -104,12 +109,12 @@ union_planner(Query *parse)
if (parse->unionClause)
{
result_plan = (Plan *) plan_union_queries(parse);
/* XXX do we need to do this? bjm 12/19/97 */
tlist = preprocess_targetlist(tlist,
parse->commandType,
parse->resultRelation,
parse->rtable);
result_plan = (Plan *) plan_union_queries(parse);
/* XXX do we need to do this? bjm 12/19/97 */
tlist = preprocess_targetlist(tlist,
parse->commandType,
parse->resultRelation,
parse->rtable);
}
else if ((rt_index =
first_inherit_rt_entry(rangetable)) != -1)
@ -117,33 +122,64 @@ union_planner(Query *parse)
result_plan = (Plan *) plan_inherit_queries(parse, rt_index);
/* XXX do we need to do this? bjm 12/19/97 */
tlist = preprocess_targetlist(tlist,
parse->commandType,
parse->resultRelation,
parse->rtable);
parse->commandType,
parse->resultRelation,
parse->rtable);
}
else
{
List **vpm = NULL;
tlist = preprocess_targetlist(tlist,
parse->commandType,
parse->resultRelation,
parse->rtable);
if (parse->rtable != NULL)
List **vpm = NULL;
/* This is only necessary if aggregates are in use in queries like:
* SELECT sid
* FROM part
* GROUP BY sid
* HAVING MIN(pid) > 1; (pid is used but never selected for!!!)
* because the function 'query_planner' creates the plan for the lefttree
* of the 'GROUP' node and returns only those attributes contained in 'tlist'.
* The original 'tlist' contains only 'sid' here and that's why we have to
* to extend it to attributes which are not selected but are used in the
* havingQual. */
/* 'check_having_qual_for_vars' takes the havingQual and the actual 'tlist'
* as arguments and recursively scans the havingQual for attributes
* (VAR nodes) that are not contained in 'tlist' yet. If so, it creates
* a new entry and attaches it to the list 'new_tlist' (consisting of the
* VAR node and the RESDOM node as usual with tlists :-) ) */
if (parse->hasAggs)
{
if (parse->havingQual != NULL)
{
vpm = (List **) palloc(length(parse->rtable) * sizeof(List *));
memset(vpm, 0, length(parse->rtable) * sizeof(List *));
new_tlist = check_having_qual_for_vars(parse->havingQual,new_tlist);
}
PlannerVarParam = lcons(vpm, PlannerVarParam);
result_plan = query_planner(parse,
parse->commandType,
tlist,
(List *) parse->qual);
PlannerVarParam = lnext(PlannerVarParam);
if (vpm != NULL)
pfree(vpm);
}
new_tlist = preprocess_targetlist(new_tlist,
parse->commandType,
parse->resultRelation,
parse->rtable);
/* Here starts the original (pre having) code */
tlist = preprocess_targetlist(tlist,
parse->commandType,
parse->resultRelation,
parse->rtable);
if (parse->rtable != NULL)
{
vpm = (List **) palloc(length(parse->rtable) * sizeof(List *));
memset(vpm, 0, length(parse->rtable) * sizeof(List *));
}
PlannerVarParam = lcons(vpm, PlannerVarParam);
result_plan = query_planner(parse,
parse->commandType,
new_tlist,
(List *) parse->qual);
PlannerVarParam = lnext(PlannerVarParam);
if (vpm != NULL)
pfree(vpm);
}
/*
* If we have a GROUP BY clause, insert a group node (with the
* appropriate sort node.)
@ -160,12 +196,12 @@ union_planner(Query *parse)
*/
tuplePerGroup = parse->hasAggs;
/* Use 'new_tlist' instead of 'tlist' */
result_plan =
make_groupPlan(&tlist,
make_groupPlan(&new_tlist,
tuplePerGroup,
parse->groupClause,
result_plan);
}
/*
@ -173,6 +209,11 @@ union_planner(Query *parse)
*/
if (parse->hasAggs)
{
int old_length=0, new_length=0;
/* Create the AGG node but use 'tlist' not 'new_tlist' as target list because we
* don't want the additional attributes (only used for the havingQual, see above)
* to show up in the result */
result_plan = (Plan *) make_agg(tlist, result_plan);
/*
@ -180,23 +221,71 @@ union_planner(Query *parse)
* the result tuple of the subplans.
*/
((Agg *) result_plan)->aggs =
set_agg_tlist_references((Agg *) result_plan);
set_agg_tlist_references((Agg *) result_plan);
if(parse->havingQual != NULL) {
List *clause;
/* set qpqual of having clause */
((Agg *) result_plan)->plan.qual=cnfify((Expr *)parse->havingQual,true);
if(parse->havingQual!=NULL)
{
List *clause;
List **vpm = NULL;
/* stuff copied from above to handle the use of attributes from outside
* in subselects */
foreach(clause, ((Agg *) result_plan)->plan.qual)
{
((Agg *) result_plan)->aggs = nconc(((Agg *) result_plan)->aggs,
check_having_qual_for_aggs((Node *) lfirst(clause),
((Agg *) result_plan)->plan.lefttree->targetlist));
}
}
}
if (parse->rtable != NULL)
{
vpm = (List **) palloc(length(parse->rtable) * sizeof(List *));
memset(vpm, 0, length(parse->rtable) * sizeof(List *));
}
PlannerVarParam = lcons(vpm, PlannerVarParam);
/* There is a subselect in the havingQual, so we have to process it
* using the same function as for a subselect in 'where' */
if (parse->hasSubLinks)
{
(List *) parse->havingQual =
(List *) SS_process_sublinks((Node *) parse->havingQual);
}
/* convert the havingQual to conjunctive normal form (cnf) */
(List *) parse->havingQual=cnfify((Expr *)(Node *) parse->havingQual,true);
/* Calculate the opfids from the opnos (=select the correct functions for
* the used VAR datatypes) */
(List *) parse->havingQual=fix_opids((List *) parse->havingQual);
((Agg *) result_plan)->plan.qual=(List *) parse->havingQual;
/* Check every clause of the havingQual for aggregates used and append
* them to result_plan->aggs */
foreach(clause, ((Agg *) result_plan)->plan.qual)
{
/* Make sure there are aggregates in the havingQual
* if so, the list must be longer after check_having_qual_for_aggs */
old_length=length(((Agg *) result_plan)->aggs);
((Agg *) result_plan)->aggs = nconc(((Agg *) result_plan)->aggs,
check_having_qual_for_aggs((Node *) lfirst(clause),
((Agg *) result_plan)->plan.lefttree->targetlist,
((List *) parse->groupClause)));
/* Have a look at the length of the returned list. If there is no
* difference, no aggregates have been found and that means, that
* the Qual belongs to the where clause */
if (((new_length=length(((Agg *) result_plan)->aggs)) == old_length) ||
(new_length == 0))
{
elog(ERROR,"This could have been done in a where clause!!");
return (Plan *)NIL;
}
}
PlannerVarParam = lnext(PlannerVarParam);
if (vpm != NULL)
pfree(vpm);
}
}
/*
* For now, before we hand back the plan, check to see if there is a
* user-specified sort that needs to be done. Eventually, this will

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.22 1998/06/15 19:28:44 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.23 1998/07/19 05:49:15 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -898,12 +898,139 @@ del_agg_clause(Node *clause)
}
/* check_having_qual_for_vars takes the the havingQual and the actual targetlist as arguments
* and recursively scans the havingQual for attributes that are not included in the targetlist
* yet. Attributes contained in the havingQual but not in the targetlist show up with queries
* like:
* SELECT sid
* FROM part
* GROUP BY sid
* HAVING MIN(pid) > 1; (pid is used but never selected for!!!).
* To be able to handle queries like that correctly we have to extend the actual targetlist
* (which will be the one used for the GROUP node later on) by these attributes. */
List *
check_having_qual_for_aggs(Node *clause, List *subplanTargetList)
check_having_qual_for_vars(Node *clause, List *targetlist_so_far)
{
List *t;
List *t;
if (IsA(clause, Var))
{
RelOptInfo tmp_rel;
tmp_rel.targetlist = targetlist_so_far;
/*
* Ha! A Var node!
*/
/* Check if the VAR is already contained in the targetlist */
if (tlist_member((Var *)clause, (List *)targetlist_so_far) == NULL)
{
add_tl_element(&tmp_rel, (Var *)clause);
}
return tmp_rel.targetlist;
}
else if (is_funcclause(clause) || not_clause(clause) ||
or_clause(clause) || and_clause(clause))
{
/*
* This is a function. Recursively call this routine for its
* arguments...
*/
foreach(t, ((Expr *) clause)->args)
{
targetlist_so_far = check_having_qual_for_vars(lfirst(t), targetlist_so_far);
}
return targetlist_so_far;
}
else if (IsA(clause, Aggreg))
{
targetlist_so_far =
check_having_qual_for_vars(((Aggreg *) clause)->target, targetlist_so_far);
return targetlist_so_far;
}
else if (IsA(clause, ArrayRef))
{
ArrayRef *aref = (ArrayRef *) clause;
/*
* This is an arrayref. Recursively call this routine for its
* expression and its index expression...
*/
foreach(t, aref->refupperindexpr)
{
targetlist_so_far = check_having_qual_for_vars(lfirst(t), targetlist_so_far);
}
foreach(t, aref->reflowerindexpr)
{
targetlist_so_far = check_having_qual_for_vars(lfirst(t), targetlist_so_far);
}
targetlist_so_far = check_having_qual_for_vars(aref->refexpr, targetlist_so_far);
targetlist_so_far = check_having_qual_for_vars(aref->refassgnexpr, targetlist_so_far);
return targetlist_so_far;
}
else if (is_opclause(clause))
{
/*
* This is an operator. Recursively call this routine for both its
* left and right operands
*/
Node *left = (Node *) get_leftop((Expr *) clause);
Node *right = (Node *) get_rightop((Expr *) clause);
if (left != (Node *) NULL)
targetlist_so_far = check_having_qual_for_vars(left, targetlist_so_far);
if (right != (Node *) NULL)
targetlist_so_far = check_having_qual_for_vars(right, targetlist_so_far);
return targetlist_so_far;
}
else if (IsA(clause, Param) || IsA(clause, Const))
{
/* do nothing! */
return targetlist_so_far;
}
/* If we get to a sublink, then we only have to check the lefthand side of the expression
* to see if there are any additional VARs */
else if (IsA(clause, SubLink))
{
foreach(t,((List *)((SubLink *)clause)->lefthand))
{
targetlist_so_far = check_having_qual_for_vars(lfirst(t), targetlist_so_far);
}
return targetlist_so_far;
}
else
{
/*
* Ooops! we can not handle that!
*/
elog(ERROR, "check_having_qual_for_vars: Can not handle this having_qual! %d\n",
nodeTag(clause));
return NIL;
}
}
/* check_having_qual_for_aggs takes the havingQual, the targetlist and the groupClause
* as arguments and scans the havingQual recursively for aggregates. If an aggregate is
* found it is attached to a list and returned by the function. (All the returned lists
* are concenated to result_plan->aggs in planner.c:union_planner() */
List *
check_having_qual_for_aggs(Node *clause, List *subplanTargetList, List *groupClause)
{
List *t, *l1;
List *agg_list = NIL;
int contained_in_group_clause = 0;
if (IsA(clause, Var))
{
TargetEntry *subplanVar;
@ -914,32 +1041,50 @@ check_having_qual_for_aggs(Node *clause, List *subplanTargetList)
subplanVar = match_varid((Var *) clause, subplanTargetList);
/*
* Change the varno & varattno fields of the var node.
*
*/
* Change the varno & varattno fields of the var node to point to the resdom->resno
* fields of the subplan (lefttree)
*/
((Var *) clause)->varattno = subplanVar->resdom->resno;
return NIL;
}
else if (is_funcclause(clause) || not_clause(clause) ||
or_clause(clause) || and_clause(clause))
{
int new_length=0, old_length=0;
/*
* This is a function. Recursively call this routine for its
* arguments...
* arguments... (i.e. for AND, OR, ... clauses!)
*/
foreach(t, ((Expr *) clause)->args)
{
agg_list = nconc(agg_list,
check_having_qual_for_aggs(lfirst(t), subplanTargetList));
old_length=length((List *)agg_list);
agg_list = nconc(agg_list,
check_having_qual_for_aggs(lfirst(t), subplanTargetList,
groupClause));
/* The arguments of OR or AND clauses are comparisons or relations
* and because we are in the havingQual there must be at least one operand
* using an aggregate function. If so, we will find it and the lenght of the
* agg_list will be increased after the above call to
* check_having_qual_for_aggs. If there are no aggregates used, the query
* could have been formulated using the 'where' clause */
if(((new_length=length((List *)agg_list)) == old_length) || (new_length == 0))
{
elog(ERROR,"This could have been done in a where clause!!");
return NIL;
}
}
return agg_list;
}
else if (IsA(clause, Aggreg))
{
return lcons(clause,
check_having_qual_for_aggs(((Aggreg *) clause)->target, subplanTargetList));
check_having_qual_for_aggs(((Aggreg *) clause)->target, subplanTargetList,
groupClause));
}
else if (IsA(clause, ArrayRef))
{
@ -952,17 +1097,21 @@ check_having_qual_for_aggs(Node *clause, List *subplanTargetList)
foreach(t, aref->refupperindexpr)
{
agg_list = nconc(agg_list,
check_having_qual_for_aggs(lfirst(t), subplanTargetList));
check_having_qual_for_aggs(lfirst(t), subplanTargetList,
groupClause));
}
foreach(t, aref->reflowerindexpr)
{
agg_list = nconc(agg_list,
check_having_qual_for_aggs(lfirst(t), subplanTargetList));
check_having_qual_for_aggs(lfirst(t), subplanTargetList,
groupClause));
}
agg_list = nconc(agg_list,
check_having_qual_for_aggs(aref->refexpr, subplanTargetList));
check_having_qual_for_aggs(aref->refexpr, subplanTargetList,
groupClause));
agg_list = nconc(agg_list,
check_having_qual_for_aggs(aref->refassgnexpr, subplanTargetList));
check_having_qual_for_aggs(aref->refassgnexpr, subplanTargetList,
groupClause));
return agg_list;
}
@ -978,27 +1127,79 @@ check_having_qual_for_aggs(Node *clause, List *subplanTargetList)
if (left != (Node *) NULL)
agg_list = nconc(agg_list,
check_having_qual_for_aggs(left, subplanTargetList));
check_having_qual_for_aggs(left, subplanTargetList,
groupClause));
if (right != (Node *) NULL)
agg_list = nconc(agg_list,
check_having_qual_for_aggs(right, subplanTargetList));
check_having_qual_for_aggs(right, subplanTargetList,
groupClause));
return agg_list;
}
else if (IsA(clause, Param) ||IsA(clause, Const))
else if (IsA(clause, Param) || IsA(clause, Const))
{
/* do nothing! */
return NIL;
}
else
{
/* This is for Sublinks which show up as EXPR nodes. All the other EXPR nodes
* (funcclauses, and_clauses, or_clauses) were caught above */
else if (IsA(clause, Expr))
{
/* Only the lefthand side of the sublink has to be checked for aggregates
* to be attached to result_plan->aggs (see planner.c:union_planner() )
*/
foreach(t,((List *)((SubLink *)((SubPlan *)
((Expr *)clause)->oper)->sublink)->lefthand))
{
agg_list =
nconc(agg_list,
check_having_qual_for_aggs(lfirst(t),
subplanTargetList, groupClause));
}
/* All arguments to the Sublink node are attributes from outside used within
* the sublink. Here we have to check that only attributes that is grouped for
* are used! */
foreach(t,((Expr *)clause)->args)
{
contained_in_group_clause = 0;
/*
* Ooops! we can not handle that!
*/
elog(ERROR, "check_having_qual_for_aggs: Can not handle this having_qual!\n");
return NIL;
}
foreach(l1,groupClause)
{
if (tlist_member(lfirst(t),lcons(((GroupClause *)lfirst(l1))->entry,NIL)) !=
NULL)
{
contained_in_group_clause=1;
}
}
/* If the use of the attribute is allowed (i.e. it is in the groupClause)
* we have to adjust the varnos and varattnos */
if (contained_in_group_clause)
{
agg_list =
nconc(agg_list,
check_having_qual_for_aggs(lfirst(t),
subplanTargetList, groupClause));
}
else
{
elog(ERROR,"You must group by the attribute used from outside!");
return NIL;
}
}
return agg_list;
}
else
{
/*
* Ooops! we can not handle that!
*/
elog(ERROR, "check_having_qual_for_aggs: Can not handle this having_qual! %d\n",
nodeTag(clause));
return NIL;
}
}

View File

@ -404,8 +404,13 @@ SS_process_sublinks(Node *expr)
((Expr *) expr)->args = (List *)
SS_process_sublinks((Node *) ((Expr *) expr)->args);
else if (IsA(expr, SubLink))/* got it! */
expr = _make_subplan((SubLink *) expr);
{
lfirst(((Expr *) lfirst(((SubLink *)expr)->oper))->args) =
lfirst(((SubLink *)expr)->lefthand);
expr = _make_subplan((SubLink *) expr);
}
return (expr);
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.76 1998/05/29 13:39:30 thomas Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.77 1998/07/19 05:49:17 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -327,6 +327,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
/* fix where clause */
qry->qual = transformWhereClause(pstate, stmt->whereClause);
/* The havingQual has a similar meaning as "qual" in the where statement.
* So we can easily use the code from the "where clause" with some additional
* traversals done in .../optimizer/plan/planner.c */
qry->havingQual = transformWhereClause(pstate, stmt->havingClause);
qry->hasSubLinks = pstate->p_hasSubLinks;
@ -356,6 +359,15 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
qry->unionall = stmt->unionall;
qry->unionClause = transformUnionClause(stmt->unionClause, qry->targetList);
/* If there is a havingQual but there are no aggregates, then there is something wrong
* with the query because having must contain aggregates in its expressions!
* Otherwise the query could have been formulated using the where clause. */
if((qry->hasAggs == false) && (qry->havingQual != NULL))
{
elog(ERROR,"This is not a valid having query!");
return (Query *)NIL;
}
return (Query *) qry;
}
@ -795,6 +807,9 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->qual = transformWhereClause(pstate, stmt->whereClause);
/* The havingQual has a similar meaning as "qual" in the where statement.
* So we can easily use the code from the "where clause" with some additional
* traversals done in .../optimizer/plan/planner.c */
qry->havingQual = transformWhereClause(pstate, stmt->havingClause);
qry->hasSubLinks = pstate->p_hasSubLinks;
@ -820,6 +835,15 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->unionall = stmt->unionall;
qry->unionClause = transformUnionClause(stmt->unionClause, qry->targetList);
/* If there is a havingQual but there are no aggregates, then there is something wrong
* with the query because having must contain aggregates in its expressions!
* Otherwise the query could have been formulated using the where clause. */
if((qry->hasAggs == false) && (qry->havingQual != NULL))
{
elog(ERROR,"This is not a valid having query!");
return (Query *)NIL;
}
return (Query *) qry;
}

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.14 1998/07/15 15:56:36 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.15 1998/07/19 05:49:22 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -2574,9 +2574,6 @@ groupby: ColId
having_clause: HAVING a_expr
{
#if FALSE
elog(ERROR,"HAVING clause not yet implemented");
#endif
$$ = $2;
}
| /*EMPTY*/ { $$ = NULL; }

View File

@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.16 1998/06/15 19:29:07 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.17 1998/07/19 05:49:24 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -342,23 +342,41 @@ ApplyRetrieveRule(Query *parsetree,
OffsetVarNodes(rule_action->qual, rt_length);
OffsetVarNodes((Node *) rule_action->targetList, rt_length);
OffsetVarNodes(rule_qual, rt_length);
OffsetVarNodes((Node *) rule_action->groupClause, rt_length);
OffsetVarNodes((Node *) rule_action->havingQual, rt_length);
ChangeVarNodes(rule_action->qual,
PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
ChangeVarNodes((Node *) rule_action->targetList,
PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
ChangeVarNodes(rule_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
ChangeVarNodes((Node *) rule_action->groupClause,
PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
ChangeVarNodes((Node *) rule_action->havingQual,
PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
if (relation_level)
{
HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
modified);
HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
modified);
}
else
{
HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
rt_index, rule->attrno, modified, &badsql);
HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
rt_index, rule->attrno, modified, &badsql);
}
if (*modified && !badsql)
AddQual(parsetree, rule_action->qual);
if (*modified && !badsql) {
AddQual(parsetree, rule_action->qual);
/* This will only work if the query made to the view defined by the following
* groupClause groups by the same attributes or does not use group at all! */
if (parsetree->groupClause == NULL)
parsetree->groupClause=rule_action->groupClause;
AddHavingQual(parsetree, rule_action->havingQual);
parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks);
}
}
static List *
@ -680,6 +698,8 @@ List *
QueryRewrite(Query *parsetree)
{
QueryRewriteSubLink(parsetree->qual);
QueryRewriteSubLink(parsetree->havingQual);
return QueryRewriteOne(parsetree);
}
@ -730,6 +750,8 @@ QueryRewriteSubLink(Node *node)
* SubLink we don't process it as part of this loop.
*/
QueryRewriteSubLink((Node *) query->qual);
QueryRewriteSubLink((Node *) query->havingQual);
ret = QueryRewriteOne(query);
if (!ret)

View File

@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.14 1998/06/15 19:29:07 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.15 1998/07/19 05:49:24 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -55,6 +55,14 @@ OffsetVarNodes(Node *node, int offset)
OffsetVarNodes(agg->target, offset);
}
break;
/* This has to be done to make queries using groupclauses work on views */
case T_GroupClause:
{
GroupClause *group = (GroupClause *) node;
OffsetVarNodes((Node *)(group->entry), offset);
}
break;
case T_Expr:
{
Expr *expr = (Expr *) node;
@ -78,6 +86,22 @@ OffsetVarNodes(Node *node, int offset)
OffsetVarNodes(lfirst(l), offset);
}
break;
case T_SubLink:
{
SubLink *sublink = (SubLink *) node;
/* We also have to adapt the variables used in sublink->lefthand
* and sublink->oper */
OffsetVarNodes((Node *)(sublink->lefthand), offset);
/* Make sure the first argument of sublink->oper points to the
* same var as sublink->lefthand does otherwise we will
* run into troubles using aggregates (aggno will not be
* set correctly) */
lfirst(((Expr *) lfirst(sublink->oper))->args) =
lfirst(sublink->lefthand);
}
break;
default:
/* ignore the others */
break;
@ -105,6 +129,16 @@ ChangeVarNodes(Node *node, int old_varno, int new_varno, int sublevels_up)
ChangeVarNodes(agg->target, old_varno, new_varno, sublevels_up);
}
break;
/* This has to be done to make queries using groupclauses work on views */
case T_GroupClause:
{
GroupClause *group = (GroupClause *) node;
ChangeVarNodes((Node *)(group->entry),old_varno, new_varno,
sublevels_up);
}
break;
case T_Expr:
{
Expr *expr = (Expr *) node;
@ -122,6 +156,8 @@ ChangeVarNodes(Node *node, int old_varno, int new_varno, int sublevels_up)
var->varno = new_varno;
var->varnoold = new_varno;
}
if (var->varlevelsup > 0) OffsetVarNodes((Node *)var,3);
}
break;
case T_List:
@ -139,6 +175,18 @@ ChangeVarNodes(Node *node, int old_varno, int new_varno, int sublevels_up)
ChangeVarNodes((Node *) query->qual, old_varno, new_varno,
sublevels_up + 1);
/* We also have to adapt the variables used in sublink->lefthand
* and sublink->oper */
ChangeVarNodes((Node *) (sublink->lefthand), old_varno, new_varno,
sublevels_up);
/* Make sure the first argument of sublink->oper points to the
* same var as sublink->lefthand does otherwise we will
* run into troubles using aggregates (aggno will not be
* set correctly */
/* lfirst(((Expr *) lfirst(sublink->oper))->args) =
lfirst(sublink->lefthand); */
}
break;
default:
@ -165,6 +213,26 @@ AddQual(Query *parsetree, Node *qual)
(Node *) make_andclause(makeList(parsetree->qual, copy, -1));
}
/* Adds the given havingQual to the one already contained in the parsetree just as
* AddQual does for the normal 'where' qual */
void
AddHavingQual(Query *parsetree, Node *havingQual)
{
Node *copy, *old;
if (havingQual == NULL)
return;
copy = copyObject(havingQual);
old = parsetree->havingQual;
if (old == NULL)
parsetree->havingQual = copy;
else
parsetree->havingQual =
(Node *) make_andclause(makeList(parsetree->havingQual, copy, -1));
}
void
AddNotQual(Query *parsetree, Node *qual)
{
@ -485,9 +553,18 @@ nodeHandleViewRule(Node **nodePtr,
Aggreg *agg = (Aggreg *) node;
nodeHandleViewRule(&(agg->target), rtable, targetlist,
rt_index, modified, sublevels_up);
rt_index, modified, sublevels_up);
}
break;
/* This has to be done to make queries using groupclauses work on views */
case T_GroupClause:
{
GroupClause *group = (GroupClause *) node;
nodeHandleViewRule((Node **) (&(group->entry)), rtable, targetlist,
rt_index, modified, sublevels_up);
}
break;
case T_Expr:
{
Expr *expr = (Expr *) node;
@ -503,20 +580,40 @@ nodeHandleViewRule(Node **nodePtr,
int this_varno = var->varno;
int this_varlevelsup = var->varlevelsup;
Node *n;
if (this_varno == rt_index &&
this_varlevelsup == sublevels_up)
{
n = FindMatchingTLEntry(targetlist,
get_attname(getrelid(this_varno,
rtable),
var->varattno));
if (n == NULL)
*nodePtr = make_null(((Var *) node)->vartype);
else
*nodePtr = n;
*modified = TRUE;
}
this_varlevelsup == sublevels_up)
{
n = FindMatchingTLEntry(targetlist,
get_attname(getrelid(this_varno,
rtable),
var->varattno));
if (n == NULL)
{
*nodePtr = make_null(((Var *) node)->vartype);
}
else
/* This is a hack: The varlevelsup of the orignal
* variable and the new one should be the same.
* Normally we adapt the node by changing a pointer
* to point to a var contained in 'targetlist'.
* In the targetlist all varlevelsups are 0
* so if we want to change it to the original value
* we have to copy the node before! (Maybe this will
* cause troubles with some sophisticated queries on
* views?) */
{
if(this_varlevelsup>0){
*nodePtr = copyObject(n);
}
else {
*nodePtr = n;
}
((Var *)*nodePtr)->varlevelsup = this_varlevelsup;
}
*modified = TRUE;
}
break;
}
case T_List:
@ -537,7 +634,20 @@ nodeHandleViewRule(Node **nodePtr,
Query *query = (Query *) sublink->subselect;
nodeHandleViewRule((Node **) &(query->qual), rtable, targetlist,
rt_index, modified, sublevels_up + 1);
rt_index, modified, sublevels_up + 1);
/* We also have to adapt the variables used in sublink->lefthand
* and sublink->oper */
nodeHandleViewRule((Node **) &(sublink->lefthand), rtable,
targetlist, rt_index, modified, sublevels_up);
/* Make sure the first argument of sublink->oper points to the
* same var as sublink->lefthand does otherwise we will
* run into troubles using aggregates (aggno will not be
* set correctly */
pfree(lfirst(((Expr *) lfirst(sublink->oper))->args));
lfirst(((Expr *) lfirst(sublink->oper))->args) =
lfirst(sublink->lefthand);
}
break;
default:
@ -553,9 +663,14 @@ HandleViewRule(Query *parsetree,
int rt_index,
int *modified)
{
nodeHandleViewRule(&parsetree->qual, rtable, targetlist, rt_index,
modified, 0);
nodeHandleViewRule((Node **) (&(parsetree->targetList)), rtable, targetlist,
rt_index, modified, 0);
/* The variables in the havingQual and groupClause also have to be adapted */
nodeHandleViewRule(&parsetree->havingQual, rtable, targetlist, rt_index,
modified, 0);
nodeHandleViewRule((Node **) (&(parsetree->groupClause)), rtable, targetlist, rt_index,
modified, 0);
}

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: planmain.h,v 1.12 1998/04/15 15:29:57 momjian Exp $
* $Id: planmain.h,v 1.13 1998/07/19 05:49:25 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -64,6 +64,8 @@ extern void set_result_tlist_references(Result *resultNode);
extern List *set_agg_tlist_references(Agg *aggNode);
extern void set_agg_agglist_references(Agg *aggNode);
extern void del_agg_tlist_references(List *tlist);
extern List *check_having_qual_for_aggs(Node *clause, List *subplanTargetList);
extern List *check_having_qual_for_aggs(Node *clause,
List *subplanTargetList, List *groupClause);
extern List *check_having_qual_for_vars(Node *clause, List *targetlist_so_far);
#endif /* PLANMAIN_H */

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: rewriteManip.h,v 1.7 1998/02/26 04:43:08 momjian Exp $
* $Id: rewriteManip.h,v 1.8 1998/07/19 05:49:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -23,6 +23,8 @@ void
ChangeVarNodes(Node *node, int old_varno, int new_varno,
int sublevels_up);
void AddQual(Query *parsetree, Node *qual);
void AddHavingQual(Query *parsetree, Node *havingQual);
void AddNotQual(Query *parsetree, Node *qual);
void FixResdomTypes(List *user_tlist);
void FixNew(RewriteInfo *info, Query *parsetree);