/*------------------------------------------------------------------------- * * rewriteManip.c * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.41 1999/10/01 04:08:24 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "optimizer/clauses.h" #include "parser/parsetree.h" #include "parser/parse_clause.h" #include "rewrite/rewriteManip.h" #include "utils/builtins.h" #include "utils/lsyscache.h" /* macros borrowed from expression_tree_mutator */ #define FLATCOPY(newnode, node, nodetype) \ ( (newnode) = makeNode(nodetype), \ memcpy((newnode), (node), sizeof(nodetype)) ) #define MUTATE(newfield, oldfield, fieldtype, mutator, context) \ ( (newfield) = (fieldtype) mutator((Node *) (oldfield), (context)) ) /* * OffsetVarNodes - adjust Vars when appending one query's RT to another * * Find all Var nodes in the given tree with varlevelsup == sublevels_up, * and increment their varno fields (rangetable indexes) by 'offset'. * The varnoold fields are adjusted similarly. * * NOTE: although this has the form of a walker, we cheat and modify the * Var nodes in-place. The given expression tree should have been copied * earlier to ensure that no unwanted side-effects occur! */ typedef struct { int offset; int sublevels_up; } OffsetVarNodes_context; static bool OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context) { if (node == NULL) return false; if (IsA(node, Var)) { Var *var = (Var *) node; if (var->varlevelsup == context->sublevels_up) { var->varno += context->offset; var->varnoold += context->offset; } return false; } if (IsA(node, SubLink)) { /* * Standard expression_tree_walker will not recurse into subselect, * but here we must do so. */ SubLink *sub = (SubLink *) node; if (OffsetVarNodes_walker((Node *) (sub->lefthand), context)) return true; OffsetVarNodes((Node *) (sub->subselect), context->offset, context->sublevels_up + 1); return false; } if (IsA(node, Query)) { /* Reach here after recursing down into subselect above... */ Query *qry = (Query *) node; if (OffsetVarNodes_walker((Node *) (qry->targetList), context)) return true; if (OffsetVarNodes_walker((Node *) (qry->qual), context)) return true; if (OffsetVarNodes_walker((Node *) (qry->havingQual), context)) return true; return false; } return expression_tree_walker(node, OffsetVarNodes_walker, (void *) context); } void OffsetVarNodes(Node *node, int offset, int sublevels_up) { OffsetVarNodes_context context; context.offset = offset; context.sublevels_up = sublevels_up; OffsetVarNodes_walker(node, &context); } /* * ChangeVarNodes - adjust Var nodes for a specific change of RT index * * Find all Var nodes in the given tree belonging to a specific relation * (identified by sublevels_up and rt_index), and change their varno fields * to 'new_index'. The varnoold fields are changed too. * * NOTE: although this has the form of a walker, we cheat and modify the * Var nodes in-place. The given expression tree should have been copied * earlier to ensure that no unwanted side-effects occur! */ typedef struct { int rt_index; int new_index; int sublevels_up; } ChangeVarNodes_context; static bool ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context) { if (node == NULL) return false; if (IsA(node, Var)) { Var *var = (Var *) node; if (var->varlevelsup == context->sublevels_up && var->varno == context->rt_index) { var->varno = context->new_index; var->varnoold = context->new_index; } return false; } if (IsA(node, SubLink)) { /* * Standard expression_tree_walker will not recurse into subselect, * but here we must do so. */ SubLink *sub = (SubLink *) node; if (ChangeVarNodes_walker((Node *) (sub->lefthand), context)) return true; ChangeVarNodes((Node *) (sub->subselect), context->rt_index, context->new_index, context->sublevels_up + 1); return false; } if (IsA(node, Query)) { /* Reach here after recursing down into subselect above... */ Query *qry = (Query *) node; if (ChangeVarNodes_walker((Node *) (qry->targetList), context)) return true; if (ChangeVarNodes_walker((Node *) (qry->qual), context)) return true; if (ChangeVarNodes_walker((Node *) (qry->havingQual), context)) return true; return false; } return expression_tree_walker(node, ChangeVarNodes_walker, (void *) context); } void ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up) { ChangeVarNodes_context context; context.rt_index = rt_index; context.new_index = new_index; context.sublevels_up = sublevels_up; ChangeVarNodes_walker(node, &context); } /* * Add the given qualifier condition to the query's WHERE clause */ void AddQual(Query *parsetree, Node *qual) { Node *copy, *old; if (qual == NULL) return; /* INTERSECT want's the original, but we need to copy - Jan */ copy = copyObject(qual); old = parsetree->qual; if (old == NULL) parsetree->qual = copy; else parsetree->qual = (Node *) make_andclause(makeList(old, copy, -1)); } /* * Add the given havingQual to the one already contained in the parsetree * just as AddQual does for the normal 'where' qual */ void AddHavingQual(Query *parsetree, Node *havingQual) { Node *copy, *old; if (havingQual == NULL) return; /* INTERSECT want's the original, but we need to copy - Jan */ copy = copyObject(havingQual); old = parsetree->havingQual; if (old == NULL) parsetree->havingQual = copy; else parsetree->havingQual = (Node *) make_andclause(makeList(old, copy, -1)); } #ifdef NOT_USED void AddNotHavingQual(Query *parsetree, Node *havingQual) { Node *notqual; if (havingQual == NULL) return; /* Need not copy input qual, because AddHavingQual will... */ notqual = (Node *) make_notclause((Expr *) havingQual); AddHavingQual(parsetree, notqual); } #endif void AddNotQual(Query *parsetree, Node *qual) { Node *notqual; if (qual == NULL) return; /* Need not copy input qual, because AddQual will... */ notqual = (Node *) make_notclause((Expr *) qual); AddQual(parsetree, notqual); } /* * Add all expressions used by the given GroupClause list to the * parsetree's targetlist and groupclause list. * * tlist is the old targetlist associated with the input groupclauses. * * XXX shouldn't we be checking to see if there are already matching * entries in parsetree->targetlist? */ void AddGroupClause(Query *parsetree, List *group_by, List *tlist) { List *l; foreach(l, group_by) { GroupClause *groupclause = (GroupClause *) copyObject(lfirst(l)); Index refnumber = groupclause->tleSortGroupRef; TargetEntry *tle = NULL; List *tl; /* Find and copy the groupclause's TLE in the old tlist */ foreach(tl, tlist) { if (((TargetEntry *) lfirst(tl))->resdom->ressortgroupref == refnumber) { tle = (TargetEntry *) copyObject(lfirst(tl)); break; } } if (tle == NULL) elog(ERROR, "AddGroupClause(): GROUP BY entry not found in rules targetlist"); /* The ressortgroupref number in the old tlist might be already * taken in the new tlist, so force assignment of a new number. */ tle->resdom->ressortgroupref = 0; groupclause->tleSortGroupRef = assignSortGroupRef(tle, parsetree->targetList); /* Also need to set the resno and mark it resjunk. */ tle->resdom->resno = length(parsetree->targetList) + 1; tle->resdom->resjunk = true; parsetree->targetList = lappend(parsetree->targetList, tle); parsetree->groupClause = lappend(parsetree->groupClause, groupclause); } } static Node * make_null(Oid type) { Const *c = makeNode(Const); c->consttype = type; c->constlen = get_typlen(type); c->constvalue = PointerGetDatum(NULL); c->constisnull = true; c->constbyval = get_typbyval(type); return (Node *) c; } #ifdef NOT_USED void FixResdomTypes(List *tlist) { List *i; foreach(i, tlist) { TargetEntry *tle = lfirst(i); if (nodeTag(tle->expr) == T_Var) { Var *var = (Var *) tle->expr; tle->resdom->restype = var->vartype; tle->resdom->restypmod = var->vartypmod; } } } #endif /* Find a targetlist entry by resno */ static Node * FindMatchingNew(List *tlist, int attno) { List *i; foreach(i, tlist) { TargetEntry *tle = lfirst(i); if (tle->resdom->resno == attno) return tle->expr; } return NULL; } /* Find a targetlist entry by resname */ static Node * FindMatchingTLEntry(List *tlist, char *e_attname) { List *i; foreach(i, tlist) { TargetEntry *tle = lfirst(i); char *resname; resname = tle->resdom->resname; if (!strcmp(e_attname, resname)) return tle->expr; } return NULL; } /* * ResolveNew - replace Vars with corresponding items from a targetlist * * Vars matching info->new_varno and sublevels_up are replaced by the * entry with matching resno from targetlist, if there is one. */ typedef struct { RewriteInfo *info; List *targetlist; int sublevels_up; } ResolveNew_context; static Node * ResolveNew_mutator(Node *node, ResolveNew_context *context) { if (node == NULL) return NULL; if (IsA(node, Var)) { Var *var = (Var *) node; int this_varno = (int) var->varno; int this_varlevelsup = (int) var->varlevelsup; if (this_varno == context->info->new_varno && this_varlevelsup == context->sublevels_up) { Node *n = FindMatchingNew(context->targetlist, var->varattno); if (n == NULL) { if (context->info->event == CMD_UPDATE) { /* For update, just change unmatched var's varno */ n = copyObject(node); ((Var *) n)->varno = context->info->current_varno; ((Var *) n)->varnoold = context->info->current_varno; return n; } else { /* Otherwise replace unmatched var with a null */ return make_null(var->vartype); } } else { /* Make a copy of the tlist item to return */ n = copyObject(n); if (IsA(n, Var)) { ((Var *) n)->varlevelsup = this_varlevelsup; } /* XXX what to do if tlist item is NOT a var? * Should we be using something like apply_RIR_adjust_sublevel? */ return n; } } /* otherwise fall through to copy the var normally */ } /* * Since expression_tree_mutator won't touch subselects, we have to * handle them specially. */ if (IsA(node, SubLink)) { SubLink *sublink = (SubLink *) node; SubLink *newnode; FLATCOPY(newnode, sublink, SubLink); MUTATE(newnode->lefthand, sublink->lefthand, List *, ResolveNew_mutator, context); context->sublevels_up++; MUTATE(newnode->subselect, sublink->subselect, Node *, ResolveNew_mutator, context); context->sublevels_up--; return (Node *) newnode; } if (IsA(node, Query)) { Query *query = (Query *) node; Query *newnode; /* * XXX original code for ResolveNew only recursed into qual field * of subquery. I'm assuming that was an oversight ... tgl 9/99 */ FLATCOPY(newnode, query, Query); MUTATE(newnode->targetList, query->targetList, List *, ResolveNew_mutator, context); MUTATE(newnode->qual, query->qual, Node *, ResolveNew_mutator, context); MUTATE(newnode->havingQual, query->havingQual, Node *, ResolveNew_mutator, context); return (Node *) newnode; } return expression_tree_mutator(node, ResolveNew_mutator, (void *) context); } static Node * ResolveNew(Node *node, RewriteInfo *info, List *targetlist, int sublevels_up) { ResolveNew_context context; context.info = info; context.targetlist = targetlist; context.sublevels_up = sublevels_up; return ResolveNew_mutator(node, &context); } void FixNew(RewriteInfo *info, Query *parsetree) { info->rule_action->targetList = (List *) ResolveNew((Node *) info->rule_action->targetList, info, parsetree->targetList, 0); info->rule_action->qual = ResolveNew(info->rule_action->qual, info, parsetree->targetList, 0); /* XXX original code didn't fix havingQual; presumably an oversight? */ info->rule_action->havingQual = ResolveNew(info->rule_action->havingQual, info, parsetree->targetList, 0); } /* * HandleRIRAttributeRule * Replace Vars matching a given RT index with copies of TL expressions. * * Handles 'on retrieve to relation.attribute * do instead retrieve (attribute = expression) w/qual' * * XXX Why is this not unified with apply_RIR_view()? */ typedef struct { List *rtable; List *targetlist; int rt_index; int attr_num; int *modified; int *badsql; int sublevels_up; } HandleRIRAttributeRule_context; static Node * HandleRIRAttributeRule_mutator(Node *node, HandleRIRAttributeRule_context *context) { if (node == NULL) return NULL; if (IsA(node, Var)) { Var *var = (Var *) node; int this_varno = var->varno; int this_varattno = var->varattno; int this_varlevelsup = var->varlevelsup; if (this_varno == context->rt_index && this_varattno == context->attr_num && this_varlevelsup == context->sublevels_up) { if (var->vartype == 32) { /* HACK: disallow SET variables */ *context->modified = TRUE; *context->badsql = TRUE; return make_null(var->vartype); } else { NameData name_to_look_for; name_to_look_for.data[0] = '\0'; namestrcpy(&name_to_look_for, (char *) get_attname(getrelid(this_varno, context->rtable), this_varattno)); if (name_to_look_for.data[0]) { Node *n; *context->modified = TRUE; n = FindMatchingTLEntry(context->targetlist, (char *) &name_to_look_for); if (n == NULL) return make_null(var->vartype); else return copyObject(n); } } } /* otherwise fall through to copy the var normally */ } /* * Since expression_tree_mutator won't touch subselects, we have to * handle them specially. */ if (IsA(node, SubLink)) { SubLink *sublink = (SubLink *) node; SubLink *newnode; FLATCOPY(newnode, sublink, SubLink); MUTATE(newnode->lefthand, sublink->lefthand, List *, HandleRIRAttributeRule_mutator, context); context->sublevels_up++; MUTATE(newnode->subselect, sublink->subselect, Node *, HandleRIRAttributeRule_mutator, context); context->sublevels_up--; return (Node *) newnode; } if (IsA(node, Query)) { Query *query = (Query *) node; Query *newnode; /* * XXX original code for HandleRIRAttributeRule only recursed into * qual field of subquery. I'm assuming that was an oversight ... */ FLATCOPY(newnode, query, Query); MUTATE(newnode->targetList, query->targetList, List *, HandleRIRAttributeRule_mutator, context); MUTATE(newnode->qual, query->qual, Node *, HandleRIRAttributeRule_mutator, context); MUTATE(newnode->havingQual, query->havingQual, Node *, HandleRIRAttributeRule_mutator, context); return (Node *) newnode; } return expression_tree_mutator(node, HandleRIRAttributeRule_mutator, (void *) context); } void HandleRIRAttributeRule(Query *parsetree, List *rtable, List *targetlist, int rt_index, int attr_num, int *modified, int *badsql) { HandleRIRAttributeRule_context context; context.rtable = rtable; context.targetlist = targetlist; context.rt_index = rt_index; context.attr_num = attr_num; context.modified = modified; context.badsql = badsql; context.sublevels_up = 0; parsetree->targetList = (List *) HandleRIRAttributeRule_mutator((Node *) parsetree->targetList, &context); parsetree->qual = HandleRIRAttributeRule_mutator(parsetree->qual, &context); /* XXX original code did not fix havingQual ... oversight? */ parsetree->havingQual = HandleRIRAttributeRule_mutator(parsetree->havingQual, &context); }