*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: execAmi.c,v 1.28 1998/12/14 08:11:02 scrappy Exp $
+ * $Id: execAmi.c,v 1.29 1999/01/18 00:09:45 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "executor/nodeNestloop.h"
#include "executor/nodeHashjoin.h"
#include "executor/nodeHash.h"
+/***S*I***/
+#include "executor/nodeGroup.h"
+
#include "executor/nodeAgg.h"
#include "executor/nodeGroup.h"
#include "executor/nodeResult.h"
* 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(node->plan.qual != NULL){
+ qual_result = ExecQual(fix_opids(node->plan.qual), econtext);
+ }
+
if (oneTuple)
pfree(oneTuple);
}
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: outfuncs.c,v 1.58 1998/12/20 07:13:36 scrappy Exp $
+ * $Id: outfuncs.c,v 1.59 1999/01/18 00:09:45 momjian Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
node->hasSubLinks ? "true" : "false");
_outNode(str, node->unionClause);
+ appendStringInfo(str, " :intersectClause ");
+ _outNode(str, node->intersectClause);
+
appendStringInfo(str, " :limitOffset ");
_outNode(str, node->limitOffset);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.40 1998/12/14 00:01:47 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.41 1999/01/18 00:09:46 momjian Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
token = lsptok(NULL, &length); /* skip :unionClause */
local_node->unionClause = nodeRead(true);
+ /***S*I***/
+ token = lsptok(NULL, &length); /* skip :intersectClause */
+ local_node->intersectClause = nodeRead(true);
+
+
token = lsptok(NULL, &length); /* skip :limitOffset */
local_node->limitOffset = nodeRead(true);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.35 1998/09/09 03:48:01 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.36 1999/01/18 00:09:47 momjian Exp $
*
*-------------------------------------------------------------------------
*/
union_planner(Query *parse)
{
List *tlist = parse->targetList;
- int tlist_len = length(tlist);
+
+ /***S*H***/
+ /* 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;
- Index rt_index;
+ Index rt_index;
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)
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;
-
- /*
- * check_having_qual_for_vars takes the havingQual and the tlist
- * as arguments and recursively scans the havingQual for VAR nodes
- * that are not contained in tlist yet. If so, it creates a new entry
- * and attaches it to the tlist. Latter, we use tlist_len to
- * truncate tlist - ie restore actual tlist...
- */
- if (parse->hasAggs)
+ List **vpm = NULL;
+
+ /***S*H***/
+ /* 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)
{
- if (parse->havingQual != NULL)
- tlist = check_having_qual_for_vars(parse->havingQual, tlist);
+ new_tlist = check_having_qual_for_vars(parse->havingQual,new_tlist);
}
-
- 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,
- 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.)
*/
tuplePerGroup = parse->hasAggs;
+ /***S*H***/
+ /* Use 'new_tlist' instead of 'tlist' */
result_plan =
- make_groupPlan(&tlist,
+ make_groupPlan(&new_tlist,
tuplePerGroup,
parse->groupClause,
result_plan);
*/
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);
/*
* 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;
- List **vpm = NULL;
-
- /*
- * Restore target list: get rid of Vars added for havingQual.
- * Assumption: tlist_len > 0...
- */
- {
- List *l;
- int tlen = 0;
-
- foreach (l, ((Agg *) result_plan)->plan.targetlist)
- {
- if (++tlen == tlist_len)
- break;
- }
- lnext(l) = NIL;
- }
-
- /*
- * 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)
- parse->havingQual = SS_process_sublinks((Node *) parse->havingQual);
-
- /* convert the havingQual to conjunctive normal form (cnf) */
- parse->havingQual = (Node *) cnfify((Expr *) (Node *) parse->havingQual, true);
-
- /*
- * Calculate the opfids from the opnos (=select the correct
- * functions for the used VAR datatypes)
- */
- parse->havingQual = (Node *) 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)
- {
- ((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)));
- }
- PlannerVarParam = lnext(PlannerVarParam);
- if (vpm != NULL)
- pfree(vpm);
- }
- }
+ /***S*H***/
+ 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);
+
+
+ /* convert the havingQual to conjunctive normal form (cnf) */
+ (List *) parse->havingQual=cnfify((Expr *)(Node *) parse->havingQual,true);
+
+ /* 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);
+ }
+
+
+ /* 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
{
Plan *sortplan = make_sortplan(tlist, parse->sortClause, result_plan);
- return (Plan *) make_unique(tlist, sortplan, parse->uniqueFlag);
+ return ((Plan *) make_unique(tlist, sortplan, parse->uniqueFlag));
}
else
{
if (parse->sortClause)
- return make_sortplan(tlist, parse->sortClause, result_plan);
+ return (make_sortplan(tlist, parse->sortClause, result_plan));
else
- return (Plan *) result_plan;
+ return ((Plan *) result_plan);
}
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.29 1998/12/14 00:02:10 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.30 1999/01/18 00:09:48 momjian Exp $
*
*-------------------------------------------------------------------------
*/
return NULL;
}
-
+/***S*H***/
/* 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
+ * like:
+ * SELECT sid
* FROM part
* GROUP BY sid
- * HAVING MIN(pid) > 1; (pid is used but never selected for!!!).
+ * 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. */
+ * (which will be the one used for the GROUP node later on) by these attributes. */
List *
check_having_qual_for_vars(Node *clause, List *targetlist_so_far)
{
- List *t;
+ List *t;
- if (IsA(clause, Var))
- {
- RelOptInfo tmp_rel;
+ if (IsA(clause, Var))
+ {
+ RelOptInfo tmp_rel;
+
+ tmp_rel.targetlist = targetlist_so_far;
+
+ /*
+ * Ha! A Var node!
+ */
- 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))
+ /* Check if the VAR is already contained in the targetlist */
+ if (tlist_member((Var *)clause, (List *)targetlist_so_far) == NULL)
{
-
- /*
- * 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))
+ 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(((Aggreg *) clause)->target, targetlist_so_far);
- return targetlist_so_far;
+ targetlist_so_far = check_having_qual_for_vars(lfirst(t), targetlist_so_far);
}
- else if (IsA(clause, ArrayRef))
+ 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)
{
- 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;
+ targetlist_so_far = check_having_qual_for_vars(lfirst(t), targetlist_so_far);
}
- else if (IsA(clause, Param) ||IsA(clause, Const))
+ foreach(t, aref->reflowerindexpr)
{
- /* do nothing! */
- return targetlist_so_far;
+ targetlist_so_far = check_having_qual_for_vars(lfirst(t), 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))
+ 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))
{
- 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;
+ 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
+/* 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
+ * 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 *t, *l1;
List *agg_list = NIL;
- int contained_in_group_clause = 0;
-
+ int contained_in_group_clause = 0;
+
if (IsA(clause, Var))
{
- TargetEntry *subplanVar;
-
- /*
- * Ha! A Var node!
- */
- subplanVar = match_varid((Var *) clause, subplanTargetList);
-
- /*
- * 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;
+ TargetEntry *subplanVar;
+
+ /*
+ * Ha! A Var node!
+ */
+ subplanVar = match_varid((Var *) clause, subplanTargetList);
+
+ /*
+ * 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))
+ /***S*H***/
+ 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... (i.e. for AND, OR, ... clauses!)
*/
foreach(t, ((Expr *) clause)->args)
{
- agg_list = nconc(agg_list,
- check_having_qual_for_aggs(lfirst(t), subplanTargetList,
- groupClause));
+ 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 length 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,
- groupClause));
+ check_having_qual_for_aggs(((Aggreg *) clause)->target, subplanTargetList,
+ groupClause));
}
else if (IsA(clause, ArrayRef))
{
foreach(t, aref->refupperindexpr)
{
agg_list = nconc(agg_list,
- check_having_qual_for_aggs(lfirst(t), subplanTargetList,
- groupClause));
+ 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,
- groupClause));
+ check_having_qual_for_aggs(lfirst(t), subplanTargetList,
+ groupClause));
}
agg_list = nconc(agg_list,
- check_having_qual_for_aggs(aref->refexpr, subplanTargetList,
- groupClause));
+ check_having_qual_for_aggs(aref->refexpr, subplanTargetList,
+ groupClause));
agg_list = nconc(agg_list,
- check_having_qual_for_aggs(aref->refassgnexpr, subplanTargetList,
- groupClause));
+ check_having_qual_for_aggs(aref->refassgnexpr, subplanTargetList,
+ groupClause));
return agg_list;
}
if (left != (Node *) NULL)
agg_list = nconc(agg_list,
- check_having_qual_for_aggs(left, subplanTargetList,
- groupClause));
+ check_having_qual_for_aggs(left, subplanTargetList,
+ groupClause));
if (right != (Node *) NULL)
agg_list = nconc(agg_list,
check_having_qual_for_aggs(right, subplanTargetList,
- groupClause));
+ 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
- */
+ /* 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;
- }
+ {
+ /* 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));
+ }
+
+ /* The first argument of ...->oper has also to be checked */
+ {
+ List *tmp_ptr;
+
+ foreach(tmp_ptr, ((SubLink *)((SubPlan *)
+ ((Expr *)clause)->oper)->sublink)->oper)
+ {
+ agg_list =
+ nconc(agg_list,
+ check_having_qual_for_aggs((Node *)lfirst(((Expr *)
+ lfirst(tmp_ptr))->args),
+ subplanTargetList, groupClause));
}
- return agg_list;
- }
+ }
+
+ /* 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! %d\n",
- nodeTag(clause));
- 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;
+ }
}
+/***S*H***/ /* End */
SS_process_sublinks((Node *) ((Expr *) expr)->args);
else if (IsA(expr, SubLink))/* got it! */
{
-
- /*
- * Hack to make sure expr->oper->args points to the same VAR node
- * as expr->lefthand does. Needed for subselects in the havingQual
- * when used on views. Otherwise aggregate functions will fail
- * later on (at execution time!) Reason: The rewite System makes
- * several copies of the VAR nodes and in this case it should not
- * do so :-(
- */
- if (((SubLink *) expr)->lefthand != NULL)
- {
- lfirst(((Expr *) lfirst(((SubLink *) expr)->oper))->args) =
- lfirst(((SubLink *) expr)->lefthand);
- }
expr = _make_subplan((SubLink *) expr);
}
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: analyze.c,v 1.91 1998/12/14 06:50:32 scrappy Exp $
+ * $Id: analyze.c,v 1.92 1999/01/18 00:09:49 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "parser/parse_node.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
+/***S*I***/
+#include "parser/parse_expr.h"
+#include "catalog/pg_type.h"
+#include "parse.h"
+
#include "utils/builtins.h"
#include "utils/mcxt.h"
* The INSERT INTO ... SELECT ... could have a UNION in child, so
* unionClause may be false
*/
- qry->unionall = stmt->unionall;
- qry->unionClause = transformUnionClause(stmt->unionClause, qry->targetList);
+ qry->unionall = stmt->unionall;
+
+ /***S*I***/
+ /* Just hand through the unionClause and intersectClause.
+ * We will handle it in the function Except_Intersect_Rewrite() */
+ qry->unionClause = stmt->unionClause;
+ qry->intersectClause = stmt->intersectClause;
/*
* If there is a havingQual but there are no aggregates, then there is
* unionClause may be false
*/
qry->unionall = stmt->unionall;
- qry->unionClause = transformUnionClause(stmt->unionClause, qry->targetList);
+
+ /***S*I***/
+ /* Just hand through the unionClause and intersectClause.
+ * We will handle it in the function Except_Intersect_Rewrite() */
+ qry->unionClause = stmt->unionClause;
+ qry->intersectClause = stmt->intersectClause;
/*
* If there is a havingQual but there are no aggregates, then there is
return qry;
}
+
+/***S*I***/
+/* This function steps through the tree
+ * built up by the select_w_o_sort rule
+ * and builds a list of all SelectStmt Nodes found
+ * The built up list is handed back in **select_list.
+ * If one of the SelectStmt Nodes has the 'unionall' flag
+ * set to true *unionall_present hands back 'true' */
+void
+create_select_list(Node *ptr, List **select_list, bool *unionall_present)
+{
+ if(IsA(ptr, SelectStmt)) {
+ *select_list = lappend(*select_list, ptr);
+ if(((SelectStmt *)ptr)->unionall == TRUE) *unionall_present = TRUE;
+ return;
+ }
+
+ /* Recursively call for all arguments. A NOT expr has no lexpr! */
+ if (((A_Expr *)ptr)->lexpr != NULL)
+ create_select_list(((A_Expr *)ptr)->lexpr, select_list, unionall_present);
+ create_select_list(((A_Expr *)ptr)->rexpr, select_list, unionall_present);
+}
+
+/* Changes the A_Expr Nodes to Expr Nodes and exchanges ANDs and ORs.
+ * The reason for the exchange is easy: We implement INTERSECTs and EXCEPTs
+ * by rewriting these queries to semantically equivalent queries that use
+ * IN and NOT IN subselects. To be able to use all three operations
+ * (UNIONs INTERSECTs and EXCEPTs) in one complex query we have to
+ * translate the queries into Disjunctive Normal Form (DNF). Unfortunately
+ * there is no function 'dnfify' but there is a function 'cnfify'
+ * which produces DNF when we exchange ANDs and ORs before calling
+ * 'cnfify' and exchange them back in the result.
+ *
+ * If an EXCEPT or INTERSECT is present *intersect_present
+ * hands back 'true' */
+Node *A_Expr_to_Expr(Node *ptr, bool *intersect_present)
+{
+ Node *result;
+
+ switch(nodeTag(ptr))
+ {
+ case T_A_Expr:
+ {
+ A_Expr *a = (A_Expr *)ptr;
+
+ switch (a->oper)
+ {
+ case AND:
+ {
+ Expr *expr = makeNode(Expr);
+ Node *lexpr = A_Expr_to_Expr(((A_Expr *)ptr)->lexpr, intersect_present);
+ Node *rexpr = A_Expr_to_Expr(((A_Expr *)ptr)->rexpr, intersect_present);
+
+ *intersect_present = TRUE;
+
+ expr->typeOid = BOOLOID;
+ expr->opType = OR_EXPR;
+ expr->args = makeList(lexpr, rexpr, -1);
+ result = (Node *) expr;
+ break;
+ }
+ case OR:
+ {
+ Expr *expr = makeNode(Expr);
+ Node *lexpr = A_Expr_to_Expr(((A_Expr *)ptr)->lexpr, intersect_present);
+ Node *rexpr = A_Expr_to_Expr(((A_Expr *)ptr)->rexpr, intersect_present);
+
+ expr->typeOid = BOOLOID;
+ expr->opType = AND_EXPR;
+ expr->args = makeList(lexpr, rexpr, -1);
+ result = (Node *) expr;
+ break;
+ }
+ case NOT:
+ {
+ Expr *expr = makeNode(Expr);
+ Node *rexpr = A_Expr_to_Expr(((A_Expr *)ptr)->rexpr, intersect_present);
+
+ expr->typeOid = BOOLOID;
+ expr->opType = NOT_EXPR;
+ expr->args = makeList(rexpr, -1);
+ result = (Node *) expr;
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ result = ptr;
+ }
+ }
+ return result;
+}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.42 1999/01/05 15:46:25 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.43 1999/01/18 00:09:51 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
#include "access/xact.h"
#include "storage/lmgr.h"
#include "utils/numeric.h"
+#include "parser/analyze.h"
#ifdef MULTIBYTE
#include "mb/pg_wchar.h"
ProcedureStmt, RecipeStmt, RemoveAggrStmt, RemoveOperStmt,
RemoveFuncStmt, RemoveStmt,
RenameStmt, RevokeStmt, RuleStmt, TransactionStmt, ViewStmt, LoadStmt,
- CreatedbStmt, DestroydbStmt, VacuumStmt, CursorStmt, SubSelect, SubUnion,
- UpdateStmt, InsertStmt, SelectStmt, NotifyStmt, DeleteStmt, ClusterStmt,
- ExplainStmt, VariableSetStmt, VariableShowStmt, VariableResetStmt,
+ CreatedbStmt, DestroydbStmt, VacuumStmt, CursorStmt, SubSelect,
+ UpdateStmt, InsertStmt, select_w_o_sort, SelectStmt, NotifyStmt, DeleteStmt,
+ ClusterStmt, ExplainStmt, VariableSetStmt, VariableShowStmt, VariableResetStmt,
CreateUserStmt, AlterUserStmt, DropUserStmt
%type <str> opt_database1, opt_database2, location, encoding
%type <boolean> TriggerForOpt, TriggerForType
-%type <list> union_clause, select_list, for_update_clause
+%type <list> for_update_clause
%type <list> join_list
%type <joinusing>
join_using
CONSTRAINT, CREATE, CROSS, CURRENT, CURRENT_DATE, CURRENT_TIME,
CURRENT_TIMESTAMP, CURRENT_USER, CURSOR,
DAY_P, DECIMAL, DECLARE, DEFAULT, DELETE, DESC, DISTINCT, DOUBLE, DROP,
- ELSE, END_TRANS, EXECUTE, EXISTS, EXTRACT,
+ ELSE, END_TRANS, EXCEPT, EXECUTE, EXISTS, EXTRACT,
FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
GRANT, GROUP, HAVING, HOUR_P,
- IN, INNER_P, INSENSITIVE, INSERT, INTERVAL, INTO, IS, ISOLATION,
- JOIN, KEY, LANGUAGE, LEADING, LEFT, LEVEL, LIKE, LOCAL,
+ IN, INNER_P, INSENSITIVE, INSERT, INTERSECT, INTERVAL, INTO, IS,
+ ISOLATION, JOIN, KEY, LANGUAGE, LEADING, LEFT, LEVEL, LIKE, LOCAL,
MATCH, MINUTE_P, MONTH_P, NAMES,
NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULLIF, NULL_P, NUMERIC,
OF, ON, ONLY, OPTION, OR, ORDER, OUTER_P,
%left '.'
%left '[' ']'
%nonassoc TYPECAST
-%left UNION
+%left UNION INTERSECT EXCEPT
%%
stmtblock: stmtmulti
stmtmulti: stmtmulti stmt ';'
{ $$ = lappend($1, $2); }
- | stmtmulti stmt
- { $$ = lappend($1, $2); }
+/***S*I***/
+/* We comment the next rule because it seems to be redundant
+ * and produces 16 shift/reduce conflicts with the new SelectStmt rule
+ * needed for EXCEPT and INTERSECTS. So far I did not notice any
+ * violations by removing the rule! */
+/* | stmtmulti stmt
+ { $$ = lappend($1, $2); } */
| stmt ';'
{ $$ = lcons($1,NIL); }
;
OptStmtList: NOTHING { $$ = NIL; }
| OptimizableStmt { $$ = lcons($1, NIL); }
| '[' OptStmtBlock ']' { $$ = $2; }
- | '(' OptStmtBlock ')' { $$ = $2; }
+/***S*I*D***/
+/* We comment this out because it produces a shift / reduce conflict
+ * with the select_w_o_sort rule */
+/* | '(' OptStmtBlock ')' { $$ = $2; } */
;
OptStmtBlock: OptStmtMulti
OptStmtMulti: OptStmtMulti OptimizableStmt ';'
{ $$ = lappend($1, $2); }
- | OptStmtMulti OptimizableStmt
- { $$ = lappend($1, $2); }
+/***S*I***/
+/* We comment the next rule because it seems to be redundant
+ * and produces 16 shift/reduce conflicts with the new SelectStmt rule
+ * needed for EXCEPT and INTERSECT. So far I did not notice any
+ * violations by removing the rule! */
+/* | OptStmtMulti OptimizableStmt
+ { $$ = lappend($1, $2); } */
| OptimizableStmt ';'
{ $$ = lcons($1, NIL); }
;
*
*****************************************************************************/
-InsertStmt: INSERT INTO relation_name opt_column_list insert_rest
+/***S*I***/
+/* This rule used 'opt_column_list' between 'relation_name' and 'insert_rest'
+ * originally. When the second rule of 'insert_rest' was changed to use
+ * the new 'SelectStmt' rule (for INTERSECT and EXCEPT) it produced a shift/reduce
+ * conflict. So I just changed the rules 'InsertStmt' and 'insert_rest' to accept
+ * the same statements without any shift/reduce conflicts */
+InsertStmt: INSERT INTO relation_name insert_rest
{
- $5->relname = $3;
- $5->cols = $4;
- $$ = (Node *)$5;
+ $4->relname = $3;
+ $$ = (Node *)$4;
}
;
insert_rest: VALUES '(' res_target_list2 ')'
{
$$ = makeNode(InsertStmt);
+ $$->cols = NULL;
$$->unique = NULL;
$$->targetList = $3;
$$->fromClause = NIL;
$$->groupClause = NIL;
$$->havingClause = NULL;
$$->unionClause = NIL;
+ /***S*I***/
+ $$->intersectClause = NIL;
}
- | SELECT opt_unique res_target_list2
- from_clause where_clause
- group_clause having_clause
- union_clause
+ /***S*I***/
+ /* We want the full power of SelectStatements including INTERSECT and EXCEPT
+ * for insertion */
+ | SelectStmt
{
+ SelectStmt *n;
+
+ n = (SelectStmt *)$1;
$$ = makeNode(InsertStmt);
- $$->unique = $2;
- $$->targetList = $3;
- $$->fromClause = $4;
- $$->whereClause = $5;
- $$->groupClause = $6;
- $$->havingClause = $7;
- $$->unionClause = $8;
+ $$->cols = NULL;
+ $$->unique = n->unique;
+ $$->targetList = n->targetList;
+ $$->fromClause = n->fromClause;
+ $$->whereClause = n->whereClause;
+ $$->groupClause = n->groupClause;
+ $$->havingClause = n->havingClause;
+ $$->unionClause = n->unionClause;
+ $$->intersectClause = n->intersectClause;
+ }
+ | '(' columnList ')' VALUES '(' res_target_list2 ')'
+ {
+ $$ = makeNode(InsertStmt);
+ $$->cols = $2;
+ $$->unique = NULL;
+ $$->targetList = $6;
+ $$->fromClause = NIL;
+ $$->whereClause = NULL;
+ $$->groupClause = NIL;
+ $$->havingClause = NULL;
+ $$->unionClause = NIL;
+ /***S*I***/
+ $$->intersectClause = NIL;
+ }
+ | '(' columnList ')' SelectStmt
+ {
+ SelectStmt *n;
+
+ n = (SelectStmt *)$4;
+ $$ = makeNode(InsertStmt);
+ $$->cols = $2;
+ $$->unique = n->unique;
+ $$->targetList = n->targetList;
+ $$->fromClause = n->fromClause;
+ $$->whereClause = n->whereClause;
+ $$->groupClause = n->groupClause;
+ $$->havingClause = n->havingClause;
+ $$->unionClause = n->unionClause;
+ $$->intersectClause = n->intersectClause;
}
;
* CURSOR STATEMENTS
*
*****************************************************************************/
-CursorStmt: DECLARE name opt_cursor CURSOR FOR
- SELECT opt_unique res_target_list2
- from_clause where_clause
- group_clause having_clause
- union_clause sort_clause
- cursor_clause
- {
- SelectStmt *n = makeNode(SelectStmt);
-
- /* from PORTAL name */
- /*
- * 15 august 1991 -- since 3.0 postgres does locking
+/***S*I***/
+CursorStmt: DECLARE name opt_cursor CURSOR FOR SelectStmt cursor_clause
+ {
+ SelectStmt *n;
+
+ n= (SelectStmt *)$6;
+ /* from PORTAL name */
+ /*
+ * 15 august 1991 -- since 3.0 postgres does locking
* right, we discovered that portals were violating
* locking protocol. portal locks cannot span xacts.
* as a short-term fix, we installed the check here.
n->portalname = $2;
n->binary = $3;
- n->unique = $7;
- n->targetList = $8;
- n->fromClause = $9;
- n->whereClause = $10;
- n->groupClause = $11;
- n->havingClause = $12;
- n->unionClause = $13;
- n->sortClause = $14;
$$ = (Node *)n;
}
;
* SELECT STATEMENTS
*
*****************************************************************************/
+/***S*I***/
+/* The new 'SelectStmt' rule adapted for the optional use of INTERSECT EXCEPT and UNION
+ * accepts the use of '(' and ')' to select an order of set operations.
+ *
+ * The rule returns a SelectStmt Node having the set operations attached to
+ * unionClause and intersectClause (NIL if no set operations were present) */
+SelectStmt: select_w_o_sort sort_clause for_update_clause
+ {
+ /* There were no set operations, so just attach the sortClause */
+ if IsA($1, SelectStmt)
+ {
+ SelectStmt *n = (SelectStmt *)$1;
+ n->sortClause = $2;
+ n->forUpdate = $3;
+ $$ = (Node *)n;
+ }
+ /* There were set operations: The root of the operator tree
+ * is delivered by $1 but we cannot hand back an A_Expr Node.
+ * So we search for the leftmost 'SelectStmt' in the operator
+ * tree $1 (which is the first Select Statement in the query
+ * typed in by the user or where ever it came from).
+ *
+ * Then we attach the whole operator tree to 'intersectClause',
+ * and a list of all 'SelectStmt' Nodes to 'unionClause' and
+ * hand back the leftmost 'SelectStmt' Node. (We do it this way
+ * because the following functions (e.g. parse_analyze etc.)
+ * excpect a SelectStmt node and not an operator tree! The whole
+ * tree attached to 'intersectClause' won't be touched by
+ * parse_analyze() etc. until the function
+ * Except_Intersect_Rewrite() (in rewriteHandler.c) which performs
+ * the necessary steps to be able create a plan!) */
+ else
+ {
+ List *select_list = NIL;
+ SelectStmt *first_select;
+ Node *op = (Node *) $1;
+ bool intersect_present = FALSE, unionall_present = FALSE;
+
+ /* Take the operator tree as an argument and
+ * create a list of all SelectStmt Nodes found in the tree.
+ *
+ * If one of the SelectStmt Nodes has the 'unionall' flag
+ * set to true the 'unionall_present' flag is also set to
+ * true */
+ create_select_list((Node *)op, &select_list, &unionall_present);
+
+ /* Replace all the A_Expr Nodes in the operator tree by
+ * Expr Nodes.
+ *
+ * If an INTERSECT or an EXCEPT is present, the
+ * 'intersect_present' flag is set to true */
+ op = A_Expr_to_Expr(op, &intersect_present);
+
+ /* If both flags are set to true we have a UNION ALL
+ * statement mixed up with INTERSECT or EXCEPT
+ * which can not be handled at the moment */
+ if (intersect_present && unionall_present)
+ {
+ elog(ERROR,"UNION ALL not allowed in mixed set operations!");
+ }
+
+ /* Get the leftmost SeletStmt Node (which automatically
+ * represents the first Select Statement of the query!) */
+ first_select = (SelectStmt *)lfirst(select_list);
+
+ /* Attach the list of all SeletStmt Nodes to unionClause */
+ first_select->unionClause = select_list;
+
+ /* Attach the whole operator tree to intersectClause */
+ first_select->intersectClause = (List *) op;
+
+ /* finally attach the sort clause */
+ first_select->sortClause = $2;
+ first_select>forUpdate = $3;
+ $$ = (Node *)first_select;
+ }
+ if ((SelectStmt *)$$)->forUpdate != NULL)
+ {
+ SelectStmt *n = (SelectStmt *)$1;
+
+ if (n->unionClause != NULL)
+ elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION clause");
+ if (n->unique != NULL)
+ elog(ERROR, "SELECT FOR UPDATE is not allowed with DISTINCT clause");
+ if (n->groupClause != NULL)
+ elog(ERROR, "SELECT FOR UPDATE is not allowed with GROUP BY clause");
+ if (n->havingClause != NULL)
+ elog(ERROR, "SELECT FOR UPDATE is not allowed with HAVING clause");
+ }
+ }
+ ;
+
+/***S*I***/
+/* This rule parses Select statements including UNION INTERSECT and EXCEPT.
+ * '(' and ')' can be used to specify the order of the operations
+ * (UNION EXCEPT INTERSECT). Without the use of '(' and ')' we want the
+ * operations to be left associative.
+ *
+ * The sort_clause is not handled here!
+ *
+ * The rule builds up an operator tree using A_Expr Nodes. AND Nodes represent
+ * INTERSECTs OR Nodes represent UNIONs and AND NOT nodes represent EXCEPTs.
+ * The SelectStatements to be connected are the left and right arguments to
+ * the A_Expr Nodes.
+ * If no set operations show up in the query the tree consists only of one
+ * SelectStmt Node */
+select_w_o_sort: '(' select_w_o_sort ')'
+ {
+ $$ = $2;
+ }
+ | SubSelect
+ {
+ $$ = $1;
+ }
+ | select_w_o_sort EXCEPT select_w_o_sort
+ {
+ $$ = (Node *)makeA_Expr(AND,NULL,$1,
+ makeA_Expr(NOT,NULL,NULL,$3));
+ }
+ | select_w_o_sort UNION opt_union select_w_o_sort
+ {
+ if (IsA($4, SelectStmt))
+ {
+ SelectStmt *n = (SelectStmt *)$4;
+ n->unionall = $3;
+ }
+ $$ = (Node *)makeA_Expr(OR,NULL,$1,$4);
+ }
+ | select_w_o_sort INTERSECT select_w_o_sort
+ {
+ $$ = (Node *)makeA_Expr(AND,NULL,$1,$3);
+ }
+ ;
-SelectStmt: SELECT opt_unique res_target_list2
+/***S*I***/
+SubSelect: SELECT opt_unique res_target_list2
result from_clause where_clause
group_clause having_clause
- union_clause sort_clause for_update_clause
{
SelectStmt *n = makeNode(SelectStmt);
n->unique = $2;
+ n->unionall = FALSE;
n->targetList = $3;
+ /***S*I***/
+ /* This is new: Subselects support the INTO clause
+ * which allows queries that are not part of the
+ * SQL92 standard and should not be formulated!
+ * We need it for INTERSECT and EXCEPT and I did not
+ * want to create a new rule 'SubSelect1' including the
+ * feature. If it makes troubles we will have to add
+ * a new rule and change this to prevent INTOs in
+ * Subselects again */
n->into = $4;
+
n->fromClause = $5;
n->whereClause = $6;
n->groupClause = $7;
n->havingClause = $8;
- n->unionClause = $9;
- n->sortClause = $10;
- n->forUpdate = $11;
- if (n->forUpdate != NULL)
- {
- if (n->unionClause != NULL)
- elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION clause");
- if (n->unique != NULL)
- elog(ERROR, "SELECT FOR UPDATE is not allowed with DISTINCT clause");
- if (n->groupClause != NULL)
- elog(ERROR, "SELECT FOR UPDATE is not allowed with GROUP BY clause");
- if (n->havingClause != NULL)
- elog(ERROR, "SELECT FOR UPDATE is not allowed with HAVING clause");
- }
- else
- $$ = (Node *)n;
- }
- ;
-
-SubSelect: SELECT opt_unique res_target_list2
- from_clause where_clause
- group_clause having_clause
- union_clause
- {
- SelectStmt *n = makeNode(SelectStmt);
- n->unique = $2;
- n->targetList = $3;
- n->fromClause = $4;
- n->whereClause = $5;
- n->groupClause = $6;
- n->havingClause = $7;
- n->unionClause = $8;
- $$ = (Node *)n;
- }
- ;
-
-union_clause: UNION opt_union select_list
- {
- SelectStmt *n = (SelectStmt *)lfirst($3);
- n->unionall = $2;
- $$ = $3;
- }
- | /*EMPTY*/
- { $$ = NIL; }
- ;
-
-select_list: select_list UNION opt_union SubUnion
- {
- SelectStmt *n = (SelectStmt *)$4;
- n->unionall = $3;
- $$ = lappend($1, $4);
- }
- | SubUnion
- { $$ = lcons($1, NIL); }
- ;
-
-SubUnion: SELECT opt_unique res_target_list2
- from_clause where_clause
- group_clause having_clause
- {
- SelectStmt *n = makeNode(SelectStmt);
- n->unique = $2;
- n->unionall = FALSE;
- n->targetList = $3;
- n->fromClause = $4;
- n->whereClause = $5;
- n->groupClause = $6;
- n->havingClause = $7;
$$ = (Node *)n;
}
;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.50 1998/12/18 09:10:34 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.51 1999/01/18 00:09:53 momjian Exp $
*
*-------------------------------------------------------------------------
*/
{"else", ELSE},
{"encoding", ENCODING},
{"end", END_TRANS},
+ /***S*I***/
+ {"except", EXCEPT},
+
{"execute", EXECUTE},
{"exists", EXISTS},
{"explain", EXPLAIN},
{"insensitive", INSENSITIVE},
{"insert", INSERT},
{"instead", INSTEAD},
+ /***S*I***/
+ {"intersect", INTERSECT},
+
{"interval", INTERVAL},
{"into", INTO},
{"is", IS},
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.27 1998/12/14 00:02:16 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.28 1999/01/18 00:09:54 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "parser/parse_relation.h"
#include "nodes/parsenodes.h"
+/***S*I***/
+#include "parser/parse_node.h"
+#include "parser/parse_target.h"
+
+#include "parser/analyze.h"
+#include "optimizer/prep.h"
+
#include "rewrite/rewriteSupport.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
case T_SubLink:
{
- SubLink *sub = (SubLink *)node;
+ SubLink *sub = (SubLink *)node;
+ List *tmp_lefthand, *tmp_oper;
apply_RIR_view(
(Node **)(&(sub->lefthand)),
tlist,
modified,
sublevels_up + 1);
+
+ /***S*I***/
+ tmp_lefthand = sub->lefthand;
+ foreach(tmp_oper, sub->oper)
+ {
+ lfirst(((Expr *) lfirst(tmp_oper))->args) =
+ lfirst(tmp_lefthand);
+ tmp_lefthand = lnext(tmp_lefthand);
+ }
}
break;
query = (Query *)lfirst(l);
results = lappend(results, fireRIRrules(query));
}
-
return results;
}
+/***S*I***/
+/* This function takes two targetlists as arguments and checks if the targetlists are compatible
+ * (i.e. both select for the same number of attributes and the types are compatible
+ */
+void check_targetlists_are_compatible(List *prev_target, List *current_target)
+{
+ List *next_target;
+
+ if (length(prev_target) !=
+ length(current_target))
+ elog(ERROR,"Each UNION | EXCEPT | INTERSECT query must have the same number of columns.");
+ foreach(next_target, current_target)
+ {
+ Oid itype;
+ Oid otype;
+
+ otype = ((TargetEntry *) lfirst(prev_target))->resdom->restype;
+ itype = ((TargetEntry *) lfirst(next_target))->resdom->restype;
+
+ /* one or both is a NULL column? then don't convert... */
+ if (otype == InvalidOid)
+ {
+ /* propagate a known type forward, if available */
+ if (itype != InvalidOid)
+ ((TargetEntry *) lfirst(prev_target))->resdom->restype = itype;
+#if FALSE
+ else
+ {
+ ((TargetEntry *) lfirst(prev_target))->resdom->restype = UNKNOWNOID;
+ ((TargetEntry *) lfirst(next_target))->resdom->restype = UNKNOWNOID;
+ }
+#endif
+ }
+ else if (itype == InvalidOid)
+ {
+ }
+ /* they don't match in type? then convert... */
+ else if (itype != otype)
+ {
+ Node *expr;
+
+ expr = ((TargetEntry *) lfirst(next_target))->expr;
+ expr = CoerceTargetExpr(NULL, expr, itype, otype);
+ if (expr == NULL)
+ {
+ elog(ERROR, "Unable to transform %s to %s"
+ "\n\tEach UNION | EXCEPT | INTERSECT clause must have compatible target types",
+ typeidTypeName(itype),
+ typeidTypeName(otype));
+ }
+ ((TargetEntry *) lfirst(next_target))->expr = expr;
+ ((TargetEntry *) lfirst(next_target))->resdom->restype = otype;
+ }
+
+ /* both are UNKNOWN? then evaluate as text... */
+ else if (itype == UNKNOWNOID)
+ {
+ ((TargetEntry *) lfirst(next_target))->resdom->restype = TEXTOID;
+ ((TargetEntry *) lfirst(prev_target))->resdom->restype = TEXTOID;
+ }
+ prev_target = lnext(prev_target);
+ }
+}
+
+/***S*I***/
+/* Rewrites UNION INTERSECT and EXCEPT queries to semantiacally equivalent
+ * queries that use IN and NOT IN subselects.
+ *
+ * The operator tree is attached to 'intersectClause' (see rule
+ * 'SelectStmt' in gram.y) of the 'parsetree' given as an
+ * argument. First we remember some clauses (the sortClause, the
+ * unique flag etc.) Then we translate the operator tree to DNF
+ * (disjunctive normal form) by 'cnfify'. (Note that 'cnfify' produces
+ * CNF but as we exchanged ANDs with ORs in function A_Expr_to_Expr()
+ * earlier we get DNF after exchanging ANDs and ORs again in the
+ * result.) Now we create a new query by evaluating the new operator
+ * tree which is in DNF now. For every AND we create an entry in the
+ * union list and for every OR we create an IN subselect. (NOT IN
+ * subselects are created for OR NOT nodes). The first entry of the
+ * union list is handed back but before that the remembered clauses
+ * (sortClause etc) are attached to the new top Node (Note that the
+ * new top Node can differ from the parsetree given as argument because of
+ * the translation to DNF. That's why we have to remember the sortClause or
+ * unique flag!) */
+Query *
+Except_Intersect_Rewrite (Query *parsetree)
+{
+
+ SubLink *n;
+ Query *result, *intersect_node;
+ List *elist, *intersect_list = NIL, *intersect, *intersectClause;
+ List *union_list = NIL, *sortClause;
+ List *left_expr, *right_expr, *resnames = NIL;
+ char *op, *uniqueFlag, *into;
+ bool isBinary, isPortal;
+ CmdType commandType = CMD_SELECT;
+ List *rtable_insert = NIL;
+
+ List *prev_target = NIL;
+
+ /* Remember the Resnames of the given parsetree's targetlist
+ * (these are the resnames of the first Select Statement of
+ * the query formulated by the user and he wants the columns
+ * named by these strings. The transformation to DNF can
+ * cause another Select Statment to be the top one which
+ * uses other names for its columns. Therefore we remeber
+ * the original names and attach them to the targetlist
+ * of the new topmost Node at the end of this function */
+ foreach(elist, parsetree->targetList)
+ {
+ TargetEntry *tent = (TargetEntry *)lfirst(elist);
+
+ resnames = lappend(resnames, tent->resdom->resname);
+ }
+
+ /* If the Statement is an INSERT INTO ... (SELECT...) statement
+ * using UNIONs, INTERSECTs or EXCEPTs and the transformation
+ * to DNF makes another Node to the top node we have to transform
+ * the new top node to an INSERT node and the original INSERT node
+ * to a SELECT node */
+ if (parsetree->commandType == CMD_INSERT)
+ {
+ parsetree->commandType = CMD_SELECT;
+ commandType = CMD_INSERT;
+ parsetree->resultRelation = 0;
+
+ /* The result relation ( = the one to insert into) has to be
+ * attached to the rtable list of the new top node */
+ rtable_insert = nth(length(parsetree->rtable) - 1, parsetree->rtable);
+ }
+
+ /* Save some items, to be able to attach them to the resulting top node
+ * at the end of the function */
+ sortClause = parsetree->sortClause;
+ uniqueFlag = parsetree->uniqueFlag;
+ into = parsetree->into;
+ isBinary = parsetree->isBinary;
+ isPortal = parsetree->isPortal;
+
+ /* The operator tree attached to parsetree->intersectClause is still 'raw'
+ * ( = the leaf nodes are still SelectStmt nodes instead of Query nodes)
+ * So step through the tree and transform the nodes using parse_analyze().
+ *
+ * The parsetree (given as an argument to
+ * Except_Intersect_Rewrite()) has already been transformed and
+ * transforming it again would cause troubles. So we give the 'raw'
+ * version (of the cooked parsetree) to the function to
+ * prevent an additional transformation. Instead we hand back the
+ * 'cooked' version also given as an argument to
+ * intersect_tree_analyze() */
+ intersectClause =
+ (List *)intersect_tree_analyze((Node *)parsetree->intersectClause,
+ (Node *)lfirst(parsetree->unionClause),
+ (Node *)parsetree);
+
+ /* intersectClause is no longer needed so set it to NIL */
+ parsetree->intersectClause = NIL;
+ /* unionClause will be needed later on but the list it delivered
+ * is no longer needed, so set it to NIL */
+ parsetree->unionClause = NIL;
+
+ /* Transform the operator tree to DNF (remember ANDs and ORs have been exchanged,
+ * that's why we get DNF by using cnfify)
+ *
+ * After the call, explicit ANDs are removed and all AND operands
+ * are simply items in the intersectClause list */
+ intersectClause = cnfify((Expr *)intersectClause, true);
+
+ /* For every entry of the intersectClause list we generate one entry in
+ * the union_list */
+ foreach(intersect, intersectClause)
+ {
+ /* for every OR we create an IN subselect and for every OR NOT
+ * we create a NOT IN subselect, so first extract all the Select
+ * Query nodes from the tree (that contains only OR or OR NOTs
+ * any more because we did a transformation to DNF
+ *
+ * There must be at least one node that is not negated
+ * (i.e. just OR and not OR NOT) and this node will be the first
+ * in the list returned */
+ intersect_list = NIL;
+ create_list((Node *)lfirst(intersect), &intersect_list);
+
+ /* This one will become the Select Query node, all other
+ * nodes are transformed into subselects under this node! */
+ intersect_node = (Query *)lfirst(intersect_list);
+ intersect_list = lnext(intersect_list);
+
+ /* Check if all Select Statements use the same number of attributes and
+ * if all corresponding attributes are of the same type */
+ if (prev_target)
+ check_targetlists_are_compatible(prev_target, intersect_node->targetList);
+ prev_target = intersect_node->targetList;
+ /* End of check for corresponding targetlists */
+
+ /* Transform all nodes remaining into subselects and add them to
+ * the qualifications of the Select Query node */
+ while(intersect_list != NIL) {
+
+ n = makeNode(SubLink);
+
+ /* Here we got an OR so transform it to an IN subselect */
+ if(IsA(lfirst(intersect_list), Query))
+ {
+ /* Check if all Select Statements use the same number of attributes and
+ * if all corresponding attributes are of the same type */
+ check_targetlists_are_compatible(prev_target,
+ ((Query *)lfirst(intersect_list))->targetList);
+ /* End of check for corresponding targetlists */
+
+ n->subselect = lfirst(intersect_list);
+ op = "=";
+ n->subLinkType = ANY_SUBLINK;
+ n->useor = false;
+ }
+ /* Here we got an OR NOT node so transform it to a NOT IN subselect */
+ else
+ {
+ /* Check if all Select Statements use the same number of attributes and
+ * if all corresponding attributes are of the same type */
+ check_targetlists_are_compatible(prev_target,
+ ((Query *)lfirst(((Expr *)lfirst(intersect_list))->args))->targetList);
+ /* End of check for corresponding targetlists */
+
+ n->subselect = (Node *)lfirst(((Expr *)lfirst(intersect_list))->args);
+ op = "<>";
+ n->subLinkType = ALL_SUBLINK;
+ n->useor = true;
+ }
+
+ /* Prepare the lefthand side of the Sublinks: All the entries of the
+ * targetlist must be (IN) or must not be (NOT IN) the subselect */
+ foreach(elist, intersect_node->targetList)
+ {
+ Node *expr = lfirst(elist);
+ TargetEntry *tent = (TargetEntry *)expr;
+
+ n->lefthand = lappend(n->lefthand, tent->expr);
+ }
+
+ /* The first arguments of oper also have to be created for the
+ * sublink (they are the same as the lefthand side!) */
+ left_expr = n->lefthand;
+ right_expr = ((Query *)(n->subselect))->targetList;
+
+ foreach(elist, left_expr)
+ {
+ Node *lexpr = lfirst(elist);
+ Node *rexpr = lfirst(right_expr);
+ TargetEntry *tent = (TargetEntry *) rexpr;
+ Expr *op_expr;
+
+ op_expr = make_op(op, lexpr, tent->expr);
+
+ n->oper = lappend(n->oper, op_expr);
+ right_expr = lnext(right_expr);
+ }
+
+ /* If the Select Query node has aggregates in use
+ * add all the subselects to the HAVING qual else to
+ * the WHERE qual */
+ if(intersect_node->hasAggs == false) {
+ AddQual(intersect_node, (Node *)n);
+ }
+ else {
+ AddHavingQual(intersect_node, (Node *)n);
+ }
+
+ /* Now we got sublinks */
+ intersect_node->hasSubLinks = true;
+ intersect_list = lnext(intersect_list);
+ }
+ intersect_node->intersectClause = NIL;
+ union_list = lappend(union_list, intersect_node);
+ }
+
+ /* The first entry to union_list is our new top node */
+ result = (Query *)lfirst(union_list);
+ /* attach the rest to unionClause */
+ result->unionClause = lnext(union_list);
+ /* Attach all the items remembered in the beginning of the function */
+ result->sortClause = sortClause;
+ result->uniqueFlag = uniqueFlag;
+ result->into = into;
+ result->isPortal = isPortal;
+ result->isBinary = isBinary;
+ /* The relation to insert into is attached to the range table
+ * of the new top node */
+ if (commandType == CMD_INSERT)
+ {
+ result->rtable = lappend(result->rtable, rtable_insert);
+ result->resultRelation = length(result->rtable);
+ result->commandType = commandType;
+ }
+ /* The resnames of the originally first SelectStatement are
+ * attached to the new first SelectStatement */
+ foreach(elist, result->targetList)
+ {
+ TargetEntry *tent = (TargetEntry *)lfirst(elist);
+
+ tent->resdom->resname = lfirst(resnames);
+ resnames = lnext(resnames);
+ }
+ return result;
+}
+
+/* Create a list of nodes that are either Query nodes of NOT Expr
+ * nodes followed by a Query node. The tree given in ptr contains at
+ * least one non negated Query node. This node is attached to the
+ * beginning of the list */
+
+void create_list(Node *ptr, List **intersect_list)
+{
+ List *arg;
+
+ if(IsA(ptr,Query))
+ {
+ /* The non negated node is attached at the beginning (lcons) */
+ *intersect_list = lcons(ptr, *intersect_list);
+ return;
+ }
+
+ if(IsA(ptr,Expr))
+ {
+ if(((Expr *)ptr)->opType == NOT_EXPR)
+ {
+ /* negated nodes are appended to the end (lappend) */
+ *intersect_list = lappend(*intersect_list, ptr);
+ return;
+ }
+ else
+ {
+ foreach(arg, ((Expr *)ptr)->args)
+ {
+ create_list(lfirst(arg), intersect_list);
+ }
+ return;
+ }
+ return;
+ }
+}
+
+/* The nodes given in 'tree' are still 'raw' so 'cook' them using parse_analyze().
+ * The node given in first_select has already been cooked, so don't transform
+ * it again but return a pointer to the previously cooked version given in 'parsetree'
+ * instead. */
+Node *intersect_tree_analyze(Node *tree, Node *first_select, Node *parsetree)
+{
+ Node *result = (Node *)NIL;
+ List *arg;
+
+ if(IsA(tree, SelectStmt))
+ {
+ QueryTreeList *qtree;
+
+ /* If we get to the tree given in first_select return
+ * parsetree instead of performing parse_analyze() */
+ if(tree == first_select){
+ result = parsetree;
+ }
+ else {
+ /* transform the 'raw' nodes to 'cooked' Query nodes */
+ qtree = parse_analyze(lcons(tree, NIL), NULL);
+ result = (Node *)qtree->qtrees[0];
+ }
+
+ }
+ if(IsA(tree,Expr))
+ {
+ /* Call recursively for every argument of the node */
+ foreach(arg, ((Expr *)tree)->args)
+ {
+ lfirst(arg) = intersect_tree_analyze(lfirst(arg), first_select, parsetree);
+ }
+ result = tree;
+ }
+ return result;
+}
+
+
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.23 1998/12/14 00:02:17 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.24 1999/01/18 00:09:56 momjian Exp $
*
*-------------------------------------------------------------------------
*/
case T_SubLink:
{
SubLink *sub = (SubLink *)node;
+ List *tmp_oper, *tmp_lefthand;
+ /* We also have to adapt the variables used in sub->lefthand
+ * and sub->oper */
OffsetVarNodes(
(Node *)(sub->lefthand),
offset,
(Node *)(sub->subselect),
offset,
sublevels_up + 1);
+
+ /***S*I***/
+ /* Make sure the first argument of sub->oper points to the
+ * same var as sub->lefthand does otherwise we will
+ * run into troubles using aggregates (aggno will not be
+ * set correctly) */
+ tmp_lefthand = sub->lefthand;
+ foreach(tmp_oper, sub->oper)
+ {
+ lfirst(((Expr *) lfirst(tmp_oper))->args) =
+ lfirst(tmp_lefthand);
+ tmp_lefthand = lnext(tmp_lefthand);
+ }
}
break;
case T_SubLink:
{
SubLink *sub = (SubLink *)node;
-
+ List *tmp_oper, *tmp_lefthand;
+
ChangeVarNodes(
(Node *)(sub->lefthand),
rt_index,
rt_index,
new_index,
sublevels_up + 1);
+
+ /***S*I***/
+ /* Make sure the first argument of sub->oper points to the
+ * same var as sub->lefthand does otherwise we will
+ * run into troubles using aggregates (aggno will not be
+ * set correctly) */
+ tmp_lefthand = sub->lefthand;
+ foreach(tmp_oper, sub->oper)
+ {
+ lfirst(((Expr *) lfirst(tmp_oper))->args) =
+ lfirst(tmp_lefthand);
+ tmp_lefthand = lnext(tmp_lefthand);
+ }
}
break;
if (qual == NULL)
return;
- copy = copyObject(qual);
+ /***S*I***/
+ /* copy = copyObject(qual); */
+ copy = qual;
+
old = parsetree->qual;
if (old == NULL)
parsetree->qual = copy;
if (havingQual == NULL)
return;
- copy = copyObject(havingQual);
+ /***S*I***/
+ copy = havingQual;
+ /* copy = copyObject(havingQual); */
+
old = parsetree->havingQual;
if (old == NULL)
parsetree->havingQual = copy;
(Node *) make_andclause(makeList(parsetree->havingQual, copy, -1));
}
+void
+AddNotHavingQual(Query *parsetree, Node *havingQual)
+{
+ Node *copy;
+
+ if (havingQual == NULL)
+ return;
+
+ /***S*I***/
+ /* copy = (Node *)make_notclause( (Expr *)copyObject(havingQual)); */
+ copy = (Node *) make_notclause((Expr *)havingQual);
+
+ AddHavingQual(parsetree, copy);
+}
void
AddNotQual(Query *parsetree, Node *qual)
if (qual == NULL)
return;
- copy = (Node *) make_notclause(copyObject(qual));
+ /***S*I***/
+ /* copy = (Node *) make_notclause((Expr *)copyObject(qual)); */
+ copy = (Node *) make_notclause((Expr *)qual);
AddQual(parsetree, copy);
}
rt_index, attr_num, modified, badsql, 0);
}
-
+#ifdef NOT_USED
static void
nodeHandleViewRule(Node **nodePtr,
List *rtable,
{
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) sublink->subselect;
+ List *tmp_lefthand, *tmp_oper;
+
nodeHandleViewRule((Node **) &(query->qual), rtable, targetlist,
rt_index, modified, sublevels_up + 1);
+ /***S*H*D***/
+ nodeHandleViewRule((Node **) &(query->havingQual), rtable, targetlist,
+ rt_index, modified, sublevels_up + 1);
+ nodeHandleViewRule((Node **) &(query->targetList), rtable, targetlist,
+ rt_index, modified, sublevels_up + 1);
+
+
/*
* We also have to adapt the variables used in
* sublink->lefthand and sublink->oper
* 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);
- }
+ /* pfree(lfirst(((Expr *) lfirst(sublink->oper))->args)); */
+
+ /***S*I***/
+ tmp_lefthand = sublink->lefthand;
+ foreach(tmp_oper, sublink->oper)
+ {
+ lfirst(((Expr *) lfirst(tmp_oper))->args) =
+ lfirst(tmp_lefthand);
+ tmp_lefthand = lnext(tmp_lefthand);
+ }
+ }
break;
default:
/* ignore the others */
}
}
-#ifdef NOT_USED
void
HandleViewRule(Query *parsetree,
List *rtable,
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.96 1999/01/17 06:18:42 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.97 1999/01/18 00:09:56 momjian Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
querytree = querytree_list->qtrees[i];
+ /***S*I***/
+ /* Rewrite Union, Intersect and Except Queries
+ * to normal Union Queries using IN and NOT IN subselects */
+ if(querytree->intersectClause != NIL)
+ {
+ querytree = Except_Intersect_Rewrite(querytree);
+ }
+
if (DebugPrintQuery)
{
if (DebugPrintQuery > 3)
- {
- /* Print the query string as is if query debug level > 3 */
- TPRINTF(TRACE_QUERY, "query: %s", query_string);
+ {
+ /* Print the query string as is if query debug level > 3 */
+ TPRINTF(TRACE_QUERY, "query: %s", query_string);
}
else
{
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
- puts("$Revision: 1.96 $ $Date: 1999/01/17 06:18:42 $\n");
+ puts("$Revision: 1.97 $ $Date: 1999/01/18 00:09:56 $\n");
}
/* ----------------
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: nodeGroup.h,v 1.7 1998/09/01 04:35:56 momjian Exp $
+ * $Id: nodeGroup.h,v 1.8 1999/01/18 00:10:02 momjian Exp $
*
*-------------------------------------------------------------------------
*/
extern int ExecCountSlotsGroup(Group *node);
extern void ExecEndGroup(Group *node);
extern void ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent);
+/***S*I***/
+extern void ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent);
+
#endif /* NODEGROUP_H */
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: parsenodes.h,v 1.65 1999/01/05 15:45:49 vadim Exp $
+ * $Id: parsenodes.h,v 1.66 1999/01/18 00:10:06 momjian Exp $
*
*-------------------------------------------------------------------------
*/
* BY */
Node *havingQual; /* qualification of each group */
+ /***S*I***/
+ List *intersectClause;
+
List *unionClause; /* unions are linked under the previous
* query */
Node *limitOffset; /* # of result tuples to skip */
List *groupClause; /* group by clause */
Node *havingClause; /* having conditional-expression */
List *unionClause; /* union subselect parameters */
- bool unionall; /* union without unique sort */
+ bool unionall; /* union without unique sort */
+ /***S*I***/
+ List *intersectClause;
} InsertStmt;
/* ----------------------
Node *whereClause; /* qualifications */
List *groupClause; /* group by clause */
Node *havingClause; /* having conditional-expression */
+ /***S*I***/
+ List *intersectClause;
+ List *exceptClause;
+
List *unionClause; /* union subselect parameters */
List *sortClause; /* sort clause (a list of SortGroupBy's) */
char *portalname; /* the portal (cursor) to create */
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: analyze.h,v 1.4 1998/09/01 04:37:25 momjian Exp $
+ * $Id: analyze.h,v 1.5 1999/01/18 00:10:11 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <parser/parse_node.h>
extern QueryTreeList *parse_analyze(List *pl, ParseState *parentParseState);
+/***S*I***/
+extern void create_select_list(Node *ptr, List **select_list, bool *unionall_present);
+extern Node *A_Expr_to_Expr(Node *ptr, bool *intersect_present);
#endif /* ANALYZE_H */
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: rewriteHandler.h,v 1.6 1998/09/01 04:38:01 momjian Exp $
+ * $Id: rewriteHandler.h,v 1.7 1999/01/18 00:10:12 momjian Exp $
*
*-------------------------------------------------------------------------
*/
extern List *QueryRewrite(Query *parsetree);
-
+/***S*I***/
+extern Query *Except_Intersect_Rewrite(Query *parsetree);
+extern void create_list(Node *ptr, List **intersect_list);
+extern Node *intersect_tree_analyze(Node *tree, Node *first_select, Node *parsetree);
+extern void check_targetlists_are_compatible(List *prev_target, List *current_target);
#endif /* REWRITEHANDLER_H */
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: rewriteManip.h,v 1.11 1998/10/21 16:21:29 momjian Exp $
+ * $Id: rewriteManip.h,v 1.12 1999/01/18 00:10:16 momjian Exp $
*
*-------------------------------------------------------------------------
*/
void AddHavingQual(Query *parsetree, Node *havingQual);
void AddNotQual(Query *parsetree, Node *qual);
+void AddNotHavingQual(Query *parsetree, Node *havingQual);
+
void FixNew(RewriteInfo *info, Query *parsetree);
void HandleRIRAttributeRule(Query *parsetree, List *rtable, List *targetlist,
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: elog.h,v 1.8 1998/09/01 04:39:03 momjian Exp $
+ * $Id: elog.h,v 1.9 1999/01/18 00:10:17 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#define ABORTX 0x4000 /* abort process after logging */
#endif
-#define ELOG_MAXLEN 4096
+/***S*I***/
+/* Increase this to be able to use postmaster -d 3 with complex
+ * view definitions (which are transformed to very, very large INSERT statements
+ * and if -d 3 is used the query string of these statements is printed using
+ * vsprintf which expects enough memory reserved! */
+#define ELOG_MAXLEN 12288
/* uncomment the following if you want your elog's to be timestamped */