diff options
author | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
---|---|---|
committer | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
commit | d31084e9d1118b25fd16580d9d8c2924b5740dff (patch) | |
tree | 3179e66307d54df9c7b966543550e601eb55e668 /src/backend/optimizer/plan/setrefs.c | |
download | postgresql-d31084e9d1118b25fd16580d9d8c2924b5740dff.tar.gz postgresql-d31084e9d1118b25fd16580d9d8c2924b5740dff.zip |
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/backend/optimizer/plan/setrefs.c')
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c new file mode 100644 index 00000000000..e698c930e1d --- /dev/null +++ b/src/backend/optimizer/plan/setrefs.c @@ -0,0 +1,706 @@ +/*------------------------------------------------------------------------- + * + * setrefs.c-- + * Routines to change varno/attno entries to contain references + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "nodes/pg_list.h" +#include "nodes/plannodes.h" +#include "nodes/primnodes.h" +#include "nodes/relation.h" + +#include "utils/elog.h" +#include "nodes/nodeFuncs.h" +#include "nodes/makefuncs.h" + +#include "optimizer/internal.h" +#include "optimizer/clauses.h" +#include "optimizer/clauseinfo.h" +#include "optimizer/keys.h" +#include "optimizer/planmain.h" +#include "optimizer/tlist.h" +#include "optimizer/var.h" +#include "optimizer/tlist.h" + +static void set_join_tlist_references(Join *join); +static void set_tempscan_tlist_references(SeqScan *tempscan); +static void set_temp_tlist_references(Temp *temp); +static List *replace_clause_joinvar_refs(Expr *clause, + List *outer_tlist, List *inner_tlist); +static List *replace_subclause_joinvar_refs(List *clauses, + List *outer_tlist, List *inner_tlist); +static Var *replace_joinvar_refs(Var *var, List *outer_tlist, List *inner_tlist); +static List *tlist_temp_references(Oid tempid, List *tlist); +static void replace_result_clause(List *clause, List *subplanTargetList); +static bool OperandIsInner(Node *opnd, int inner_relid); +static void replace_agg_clause(Node *expr, List *targetlist); + +/***************************************************************************** + * + * SUBPLAN REFERENCES + * + *****************************************************************************/ + +/* + * set-tlist-references-- + * Modifies the target list of nodes in a plan to reference target lists + * at lower levels. + * + * 'plan' is the plan whose target list and children's target lists will + * be modified + * + * Returns nothing of interest, but modifies internal fields of nodes. + * + */ +void +set_tlist_references(Plan *plan) +{ + if(plan==NULL) + return; + + if (IsA_Join(plan)) { + set_join_tlist_references((Join*)plan); + } else if (IsA(plan,SeqScan) && plan->lefttree && + IsA_Temp(plan->lefttree)) { + set_tempscan_tlist_references((SeqScan*)plan); + } else if (IsA(plan,Sort)) { + set_temp_tlist_references ((Temp*)plan); + } else if (IsA(plan,Result)) { + set_result_tlist_references((Result*)plan); + } else if (IsA(plan,Hash)) { + set_tlist_references(plan->lefttree); + } else if (IsA(plan,Choose)) { + List *x; + foreach (x, ((Choose*)plan)->chooseplanlist) { + set_tlist_references((Plan*)lfirst(x)); + } + } +} + +/* + * set-join-tlist-references-- + * Modifies the target list of a join node by setting the varnos and + * varattnos to reference the target list of the outer and inner join + * relations. + * + * Creates a target list for a join node to contain references by setting + * varno values to OUTER or INNER and setting attno values to the + * result domain number of either the corresponding outer or inner join + * tuple. + * + * 'join' is a join plan node + * + * Returns nothing of interest, but modifies internal fields of nodes. + * + */ +static void +set_join_tlist_references(Join *join) +{ + Plan *outer = ((Plan*)join)->lefttree; + Plan *inner = ((Plan*)join)->righttree; + List *new_join_targetlist = NIL; + TargetEntry *temp = (TargetEntry *)NULL; + List *entry = NIL; + List *inner_tlist = NULL; + List *outer_tlist = NULL; + TargetEntry *xtl = (TargetEntry *)NULL; + List *qptlist = ((Plan*)join)->targetlist; + + foreach(entry, qptlist) { + List *joinvar; + + xtl = (TargetEntry *)lfirst(entry); + inner_tlist = ((inner==NULL) ? NIL : inner->targetlist); + outer_tlist = ((outer==NULL) ? NIL : outer->targetlist); + joinvar = replace_clause_joinvar_refs((Expr*)get_expr(xtl), + outer_tlist, + inner_tlist); + + temp = MakeTLE(xtl->resdom, (Node*)joinvar); + new_join_targetlist = lappend(new_join_targetlist,temp); + } + + ((Plan*)join)->targetlist = new_join_targetlist; + if (outer!=NULL) + set_tlist_references(outer); + if (inner!=NULL) + set_tlist_references(inner); +} + +/* + * set-tempscan-tlist-references-- + * Modifies the target list of a node that scans a temp relation (i.e., a + * sort or hash node) so that the varnos refer to the child temporary. + * + * 'tempscan' is a seqscan node + * + * Returns nothing of interest, but modifies internal fields of nodes. + * + */ +static void +set_tempscan_tlist_references(SeqScan *tempscan) +{ + Temp *temp = (Temp*)((Plan*)tempscan)->lefttree; + + ((Plan*)tempscan)->targetlist = + tlist_temp_references(temp->tempid, + ((Plan*)tempscan)->targetlist); + set_temp_tlist_references(temp); +} + +/* + * set-temp-tlist-references-- + * The temp's vars are made consistent with (actually, identical to) the + * modified version of the target list of the node from which temp node + * receives its tuples. + * + * 'temp' is a temp (e.g., sort, hash) plan node + * + * Returns nothing of interest, but modifies internal fields of nodes. + * + */ +static void +set_temp_tlist_references(Temp *temp) +{ + Plan *source = ((Plan*)temp)->lefttree; + + if (source!=NULL) { + set_tlist_references(source); + ((Plan*)temp)->targetlist = + copy_vars(((Plan*)temp)->targetlist , + (source)->targetlist); + } else { + elog(WARN, "calling set_temp_tlist_references with empty lefttree"); + } +} + +/* + * join-references-- + * Creates a new set of join clauses by replacing the varno/varattno + * values of variables in the clauses to reference target list values + * from the outer and inner join relation target lists. + * + * 'clauses' is the list of join clauses + * 'outer-tlist' is the target list of the outer join relation + * 'inner-tlist' is the target list of the inner join relation + * + * Returns the new join clauses. + * + */ +List * +join_references(List *clauses, + List *outer_tlist, + List *inner_tlist) +{ + return (replace_subclause_joinvar_refs(clauses, + outer_tlist, + inner_tlist)); +} + +/* + * index-outerjoin-references-- + * Given a list of join clauses, replace the operand corresponding to the + * outer relation in the join with references to the corresponding target + * list element in 'outer-tlist' (the outer is rather obscurely + * identified as the side that doesn't contain a var whose varno equals + * 'inner-relid'). + * + * As a side effect, the operator is replaced by the regproc id. + * + * 'inner-indxqual' is the list of join clauses (so-called because they + * are used as qualifications for the inner (inbex) scan of a nestloop) + * + * Returns the new list of clauses. + * + */ +List * +index_outerjoin_references(List *inner_indxqual, + List *outer_tlist, + Index inner_relid) +{ + List *t_list = NIL; + Expr *temp = NULL; + List *t_clause = NIL; + Expr *clause = NULL; + + foreach (t_clause,inner_indxqual) { + clause = lfirst(t_clause); + /* + * if inner scan on the right. + */ + if (OperandIsInner((Node*)get_rightop(clause), inner_relid)) { + Var *joinvar = (Var*) + replace_clause_joinvar_refs((Expr*)get_leftop(clause), + outer_tlist, + NIL); + temp = make_opclause(replace_opid((Oper*)((Expr*)clause)->oper), + joinvar, + get_rightop(clause)); + t_list = lappend(t_list,temp); + } else { + /* inner scan on left */ + Var *joinvar = (Var*) + replace_clause_joinvar_refs((Expr*)get_rightop(clause), + outer_tlist, + NIL); + temp = make_opclause(replace_opid((Oper*)((Expr*)clause)->oper), + joinvar, + get_leftop(clause)); + t_list = lappend(t_list,temp); + } + + } + return(t_list); +} + +/* + * replace-clause-joinvar-refs + * replace-subclause-joinvar-refs + * replace-joinvar-refs + * + * Replaces all variables within a join clause with a new var node + * whose varno/varattno fields contain a reference to a target list + * element from either the outer or inner join relation. + * + * 'clause' is the join clause + * 'outer-tlist' is the target list of the outer join relation + * 'inner-tlist' is the target list of the inner join relation + * + * Returns the new join clause. + * + */ +static List * +replace_clause_joinvar_refs(Expr *clause, + List *outer_tlist, + List *inner_tlist) +{ + List *temp = NULL; + + if(IsA (clause,Var)) { + temp = (List*)replace_joinvar_refs((Var*)clause, + outer_tlist,inner_tlist); + if(temp) + return(temp); + else + if (clause != NULL) + return((List*)clause); + else + return(NIL); + } else if (single_node((Node*)clause)) { + return ((List*)clause); + } else if (or_clause((Node*)clause)) { + List *orclause = + replace_subclause_joinvar_refs(((Expr*)clause)->args, + outer_tlist, + inner_tlist); + return ((List*)make_orclause(orclause)); + } else if (IsA(clause,ArrayRef)) { + ArrayRef *aref = (ArrayRef *)clause; + + temp = replace_subclause_joinvar_refs(aref->refupperindexpr, + outer_tlist, + inner_tlist); + aref->refupperindexpr = (List*)temp; + temp = replace_subclause_joinvar_refs(aref->reflowerindexpr, + outer_tlist, + inner_tlist); + aref->reflowerindexpr = (List*)temp; + temp = replace_clause_joinvar_refs((Expr*)aref->refexpr, + outer_tlist, + inner_tlist); + aref->refexpr = (Node*)temp; + + /* + * no need to set refassgnexpr. we only set that in the + * target list on replaces, and this is an array reference + * in the qualification. if we got this far, it's 0x0 in + * the ArrayRef structure 'clause'. + */ + + return((List*)clause); + } else if (is_funcclause((Node*)clause)) { + List *funcclause = + replace_subclause_joinvar_refs(((Expr*)clause)->args, + outer_tlist, + inner_tlist); + return ((List*)make_funcclause((Func*)((Expr*)clause)->oper, + funcclause)); + } else if (not_clause((Node*)clause)) { + List *notclause = + replace_clause_joinvar_refs(get_notclausearg(clause), + outer_tlist, + inner_tlist); + return ((List*)make_notclause((Expr*)notclause)); + } else if (is_opclause((Node*)clause)) { + Var *leftvar = + (Var*)replace_clause_joinvar_refs((Expr*)get_leftop(clause), + outer_tlist, + inner_tlist); + Var *rightvar = + (Var*)replace_clause_joinvar_refs((Expr*)get_rightop(clause), + outer_tlist, + inner_tlist); + return ((List*)make_opclause(replace_opid((Oper*)((Expr*)clause)->oper), + leftvar, + rightvar)); + } + /* shouldn't reach here */ + return NULL; +} + +static List * +replace_subclause_joinvar_refs(List *clauses, + List *outer_tlist, + List *inner_tlist) +{ + List *t_list = NIL; + List *temp = NIL; + List *clause = NIL; + + foreach (clause,clauses) { + temp = replace_clause_joinvar_refs(lfirst(clause), + outer_tlist, + inner_tlist); + t_list = lappend(t_list,temp); + } + return(t_list); +} + +static Var * +replace_joinvar_refs(Var *var, List *outer_tlist, List *inner_tlist) +{ + Resdom *outer_resdom =(Resdom*)NULL; + + outer_resdom= tlist_member(var,outer_tlist); + + if (outer_resdom!=NULL && IsA (outer_resdom,Resdom) ) { + return (makeVar (OUTER, + outer_resdom->resno, + var->vartype, + var->varnoold, + var->varoattno)); + } else { + Resdom *inner_resdom; + inner_resdom = tlist_member(var,inner_tlist); + if ( inner_resdom!=NULL && IsA (inner_resdom,Resdom) ) { + return (makeVar (INNER, + inner_resdom->resno, + var->vartype, + var->varnoold, + var->varoattno)); + } + } + return (Var*)NULL; +} + +/* + * tlist-temp-references-- + * Creates a new target list for a node that scans a temp relation, + * setting the varnos to the id of the temp relation and setting varids + * if necessary (varids are only needed if this is a targetlist internal + * to the tree, in which case the targetlist entry always contains a var + * node, so we can just copy it from the temp). + * + * 'tempid' is the id of the temp relation + * 'tlist' is the target list to be modified + * + * Returns new target list + * + */ +static List * +tlist_temp_references(Oid tempid, + List *tlist) +{ + List *t_list = NIL; + TargetEntry *temp = (TargetEntry *)NULL; + TargetEntry *xtl = NULL; + List *entry; + + foreach (entry, tlist) { + AttrNumber oattno; + + xtl = lfirst(entry); + if (IsA(get_expr(xtl), Var)) + oattno = ((Var*)xtl->expr)->varoattno; + else + oattno = 0; + + temp = MakeTLE(xtl->resdom, + (Node*)makeVar(tempid, + xtl->resdom->resno, + xtl->resdom->restype, + tempid, + oattno)); + + t_list = lappend(t_list,temp); + } + return(t_list); +} + +/*--------------------------------------------------------- + * + * set_result_tlist_references + * + * Change the target list of a Result node, so that it correctly + * addresses the tuples returned by its left tree subplan. + * + * NOTE: + * 1) we ignore the right tree! (in the current implementation + * it is always nil + * 2) this routine will probably *NOT* work with nested dot + * fields.... + */ +void +set_result_tlist_references(Result *resultNode) +{ + Plan *subplan; + List *resultTargetList; + List *subplanTargetList; + List *t; + TargetEntry *entry; + Expr *expr; + + resultTargetList= ((Plan*)resultNode)->targetlist; + + /* + * NOTE: we only consider the left tree subplan. + * This is usually a seq scan. + */ + subplan = ((Plan*)resultNode)->lefttree; + if (subplan != NULL) { + subplanTargetList = subplan->targetlist; + } else { + subplanTargetList = NIL; + } + + /* + * now for traverse all the entris of the target list. + * These should be of the form (Resdom_Node Expression). + * For every expression clause, call "replace_result_clause()" + * to appropriatelly change all the Var nodes. + */ + foreach (t, resultTargetList) { + entry = (TargetEntry *)lfirst(t); + expr = (Expr*) get_expr(entry); + replace_result_clause((List*)expr, subplanTargetList); + } +} + +/*--------------------------------------------------------- + * + * replace_result_clause + * + * This routine is called from set_result_tlist_references(). + * and modifies the expressions of the target list of a Result + * node so that all Var nodes reference the target list of its subplan. + * + */ +static void +replace_result_clause(List *clause, + List *subplanTargetList) /* target list of the + subplan */ +{ + List *t; + List *subClause; + TargetEntry *subplanVar; + + if (IsA(clause,Var)) { + /* + * Ha! A Var node! + */ + subplanVar = match_varid((Var*)clause, subplanTargetList); + /* + * Change the varno & varattno fields of the + * var node. + * + */ + ((Var*)clause)->varno = (Index)OUTER; + ((Var*)clause)->varattno = subplanVar->resdom->resno; + } else if (is_funcclause((Node*)clause)) { + /* + * This is a function. Recursively call this routine + * for its arguments... + */ + subClause = ((Expr*)clause)->args; + foreach (t, subClause) { + replace_result_clause(lfirst(t),subplanTargetList); + } + } else if (IsA(clause,ArrayRef)) { + ArrayRef *aref = (ArrayRef *)clause; + /* + * This is an arrayref. Recursively call this routine + * for its expression and its index expression... + */ + subClause = aref->refupperindexpr; + foreach (t, subClause) { + replace_result_clause(lfirst(t),subplanTargetList); + } + subClause = aref->reflowerindexpr; + foreach (t, subClause) { + replace_result_clause(lfirst(t),subplanTargetList); + } + replace_result_clause((List*)aref->refexpr, + subplanTargetList); + replace_result_clause((List*)aref->refassgnexpr, + subplanTargetList); + } else if (is_opclause((Node*)clause)) { + /* + * This is an operator. Recursively call this routine + * for both its left and right operands + */ + subClause = (List*)get_leftop((Expr*)clause); + replace_result_clause(subClause,subplanTargetList); + subClause = (List*)get_rightop((Expr*)clause); + replace_result_clause(subClause,subplanTargetList); + } else if (IsA(clause,Param) || IsA(clause,Const)) { + /* do nothing! */ + } else { + /* + * Ooops! we can not handle that! + */ + elog(WARN,"replace_result_clause: Can not handle this tlist!\n"); + } +} + +static +bool OperandIsInner(Node *opnd, int inner_relid) +{ + /* + * Can be the inner scan if its a varnode or a function and the + * inner_relid is equal to the varnode's var number or in the + * case of a function the first argument's var number (all args + * in a functional index are from the same relation). + */ + if ( IsA (opnd,Var) && + (inner_relid == ((Var*)opnd)->varno) ) + { + return true; + } + if (is_funcclause(opnd)) + { + List *firstArg = lfirst(((Expr*)opnd)->args); + + if ( IsA (firstArg,Var) && + (inner_relid == ((Var*)firstArg)->varno) ) + { + return true; + } + } + return false; +} + +/***************************************************************************** + * + *****************************************************************************/ + +/*--------------------------------------------------------- + * + * set_agg_tlist_references - + * changes the target list of an Agg node so that it points to + * the tuples returned by its left tree subplan. + * + */ +void +set_agg_tlist_references(Agg *aggNode) +{ + List *aggTargetList; + List *subplanTargetList; + List *tl; + + aggTargetList = aggNode->plan.targetlist; + subplanTargetList = aggNode->plan.lefttree->targetlist; + + foreach (tl, aggTargetList) { + TargetEntry *tle = lfirst(tl); + + replace_agg_clause(tle->expr, subplanTargetList); + } +} + +void +set_agg_agglist_references(Agg *aggNode) +{ + List *subplanTargetList; + Aggreg **aggs; + int i; + + aggs = aggNode->aggs; + subplanTargetList = aggNode->plan.lefttree->targetlist; + + for (i = 0; i < aggNode->numAgg; i++) { + replace_agg_clause(aggs[i]->target, subplanTargetList); + } +} + +static void +replace_agg_clause(Node *clause, List *subplanTargetList) +{ + List *t; + TargetEntry *subplanVar; + + if (IsA(clause,Var)) { + /* + * Ha! A Var node! + */ + subplanVar = match_varid((Var*)clause, subplanTargetList); + /* + * Change the varno & varattno fields of the + * var node. + * + */ + ((Var*)clause)->varattno = subplanVar->resdom->resno; + } else if (is_funcclause(clause)) { + /* + * This is a function. Recursively call this routine + * for its arguments... + */ + foreach (t, ((Expr*)clause)->args) { + replace_agg_clause(lfirst(t), subplanTargetList); + } + } else if (IsA(clause,Aggreg)) { + replace_agg_clause(((Aggreg*)clause)->target, subplanTargetList); + } 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) { + replace_agg_clause(lfirst(t),subplanTargetList); + } + foreach (t, aref->reflowerindexpr) { + replace_agg_clause(lfirst(t),subplanTargetList); + } + replace_agg_clause(aref->refexpr, subplanTargetList); + replace_agg_clause(aref->refassgnexpr, subplanTargetList); + } else if (is_opclause(clause)) { + /* + * This is an operator. Recursively call this routine + * for both its left and right operands + */ + replace_agg_clause((Node*)get_leftop((Expr*)clause), + subplanTargetList); + replace_agg_clause((Node*)get_rightop((Expr*)clause), + subplanTargetList); + } else if (IsA(clause,Param) || IsA(clause,Const)) { + /* do nothing! */ + } else { + /* + * Ooops! we can not handle that! + */ + elog(WARN,"replace_agg_clause: Can not handle this tlist!\n"); + } + +} + + |