/*------------------------------------------------------------------------- * * 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.4 1997/06/12 17:26:15 vadim Exp $ * *------------------------------------------------------------------------- */ #include #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), get_leftop(clause), joinvar); 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 */ Node *left = (Node*)get_leftop((Expr*)clause); Node *right = (Node*)get_rightop((Expr*)clause); if ( left != (Node*) NULL ) replace_agg_clause(left, subplanTargetList); if ( right != (Node*) NULL ) replace_agg_clause(right, 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"); } }