aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/plan/planner.c177
-rw-r--r--src/backend/optimizer/plan/setrefs.c255
-rw-r--r--src/backend/optimizer/plan/subselect.c7
3 files changed, 367 insertions, 72 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index c88d201501c..51a93517c17 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -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);
-
- 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->havingQual!=NULL)
+ {
+ List *clause;
+ List **vpm = NULL;
+
+
+ /* stuff copied from above to handle the use of attributes from outside
+ * in subselects */
+ 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
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index c1e3194133b..96b203498ff 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -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;
}
+ /* 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;
+
+ 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!\n");
- return NIL;
- }
+ {
+ /*
+ * 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;
+ }
}
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 72d023dccf5..040c6732ccc 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -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);
}