📄 rewritemanip.c
字号:
/*------------------------------------------------------------------------- * * rewriteManip.c * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.92.2.3 2006/01/06 20:11:18 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "catalog/pg_type.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/tlist.h"#include "parser/parsetree.h"#include "parser/parse_coerce.h"#include "parser/parse_relation.h"#include "rewrite/rewriteManip.h"#include "utils/lsyscache.h"typedef struct{ int sublevels_up;} checkExprHasAggs_context;static bool checkExprHasAggs_walker(Node *node, checkExprHasAggs_context *context);static bool checkExprHasSubLink_walker(Node *node, void *context);static Relids offset_relid_set(Relids relids, int offset);static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);/* * checkExprHasAggs - * Check if an expression contains an aggregate function call. * * The objective of this routine is to detect whether there are aggregates * belonging to the initial query level. Aggregates belonging to subqueries * or outer queries do NOT cause a true result. We must recurse into * subqueries to detect outer-reference aggregates that logically belong to * the initial query level. */boolcheckExprHasAggs(Node *node){ checkExprHasAggs_context context; context.sublevels_up = 0; /* * Must be prepared to start with a Query or a bare expression tree; if * it's a Query, we don't want to increment sublevels_up. */ return query_or_expression_tree_walker(node, checkExprHasAggs_walker, (void *) &context, 0);}static boolcheckExprHasAggs_walker(Node *node, checkExprHasAggs_context *context){ if (node == NULL) return false; if (IsA(node, Aggref)) { if (((Aggref *) node)->agglevelsup == context->sublevels_up) return true; /* abort the tree traversal and return true */ /* else fall through to examine argument */ } if (IsA(node, Query)) { /* Recurse into subselects */ bool result; context->sublevels_up++; result = query_tree_walker((Query *) node, checkExprHasAggs_walker, (void *) context, 0); context->sublevels_up--; return result; } return expression_tree_walker(node, checkExprHasAggs_walker, (void *) context);}/* * checkExprHasSubLink - * Check if an expression contains a SubLink. */boolcheckExprHasSubLink(Node *node){ /* * If a Query is passed, examine it --- but we need not recurse into * sub-Queries. */ return query_or_expression_tree_walker(node, checkExprHasSubLink_walker, NULL, QTW_IGNORE_RT_SUBQUERIES);}static boolcheckExprHasSubLink_walker(Node *node, void *context){ if (node == NULL) return false; if (IsA(node, SubLink)) return true; /* abort the tree traversal and return true */ return expression_tree_walker(node, checkExprHasSubLink_walker, 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. Also, adjust other nodes * that contain rangetable indexes, such as RangeTblRef and JoinExpr. * * NOTE: although this has the form of a walker, we cheat and modify the * 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 boolOffsetVarNodes_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, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) node; if (context->sublevels_up == 0) rtr->rtindex += context->offset; /* the subquery itself is visited separately */ return false; } if (IsA(node, JoinExpr)) { JoinExpr *j = (JoinExpr *) node; if (context->sublevels_up == 0) j->rtindex += context->offset; /* fall through to examine children */ } if (IsA(node, InClauseInfo)) { InClauseInfo *ininfo = (InClauseInfo *) node; if (context->sublevels_up == 0) { ininfo->lefthand = offset_relid_set(ininfo->lefthand, context->offset); ininfo->righthand = offset_relid_set(ininfo->righthand, context->offset); } /* fall through to examine children */ } if (IsA(node, Query)) { /* Recurse into subselects */ bool result; context->sublevels_up++; result = query_tree_walker((Query *) node, OffsetVarNodes_walker, (void *) context, 0); context->sublevels_up--; return result; } return expression_tree_walker(node, OffsetVarNodes_walker, (void *) context);}voidOffsetVarNodes(Node *node, int offset, int sublevels_up){ OffsetVarNodes_context context; context.offset = offset; context.sublevels_up = sublevels_up; /* * Must be prepared to start with a Query or a bare expression tree; if * it's a Query, go straight to query_tree_walker to make sure that * sublevels_up doesn't get incremented prematurely. */ if (node && IsA(node, Query)) { Query *qry = (Query *) node; /* * If we are starting at a Query, and sublevels_up is zero, then we * must also fix rangetable indexes in the Query itself --- namely * resultRelation and rowMarks entries. sublevels_up cannot be zero * when recursing into a subquery, so there's no need to have the same * logic inside OffsetVarNodes_walker. */ if (sublevels_up == 0) { ListCell *l; if (qry->resultRelation) qry->resultRelation += offset; foreach(l, qry->rowMarks) lfirst_int(l) += offset; } query_tree_walker(qry, OffsetVarNodes_walker, (void *) &context, 0); } else OffsetVarNodes_walker(node, &context);}static Relidsoffset_relid_set(Relids relids, int offset){ Relids result = NULL; Relids tmprelids; int rtindex; tmprelids = bms_copy(relids); while ((rtindex = bms_first_member(tmprelids)) >= 0) result = bms_add_member(result, rtindex + offset); bms_free(tmprelids); return result;}/* * 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. Also, adjust other * nodes that contain rangetable indexes, such as RangeTblRef and JoinExpr. * * NOTE: although this has the form of a walker, we cheat and modify the * 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 boolChangeVarNodes_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, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) node; if (context->sublevels_up == 0 && rtr->rtindex == context->rt_index) rtr->rtindex = context->new_index; /* the subquery itself is visited separately */ return false; } if (IsA(node, JoinExpr)) { JoinExpr *j = (JoinExpr *) node; if (context->sublevels_up == 0 && j->rtindex == context->rt_index) j->rtindex = context->new_index; /* fall through to examine children */ } if (IsA(node, InClauseInfo)) { InClauseInfo *ininfo = (InClauseInfo *) node; if (context->sublevels_up == 0) { ininfo->lefthand = adjust_relid_set(ininfo->lefthand, context->rt_index, context->new_index); ininfo->righthand = adjust_relid_set(ininfo->righthand, context->rt_index, context->new_index); } /* fall through to examine children */ } if (IsA(node, Query)) { /* Recurse into subselects */ bool result; context->sublevels_up++; result = query_tree_walker((Query *) node, ChangeVarNodes_walker, (void *) context, 0); context->sublevels_up--; return result; } return expression_tree_walker(node, ChangeVarNodes_walker, (void *) context);}voidChangeVarNodes(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; /* * Must be prepared to start with a Query or a bare expression tree; if * it's a Query, go straight to query_tree_walker to make sure that * sublevels_up doesn't get incremented prematurely. */ if (node && IsA(node, Query)) { Query *qry = (Query *) node; /* * If we are starting at a Query, and sublevels_up is zero, then we * must also fix rangetable indexes in the Query itself --- namely * resultRelation and rowMarks entries. sublevels_up cannot be zero * when recursing into a subquery, so there's no need to have the same * logic inside ChangeVarNodes_walker. */ if (sublevels_up == 0) { ListCell *l; if (qry->resultRelation == rt_index) qry->resultRelation = new_index; foreach(l, qry->rowMarks) { if (lfirst_int(l) == rt_index) lfirst_int(l) = new_index; } } query_tree_walker(qry, ChangeVarNodes_walker, (void *) &context, 0); } else ChangeVarNodes_walker(node, &context);}/* * Substitute newrelid for oldrelid in a Relid set */static Relidsadjust_relid_set(Relids relids, int oldrelid, int newrelid){ if (bms_is_member(oldrelid, relids)) { /* Ensure we have a modifiable copy */ relids = bms_copy(relids); /* Remove old, add new */ relids = bms_del_member(relids, oldrelid); relids = bms_add_member(relids, newrelid); } return relids;}/* * IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree * * Find all Var nodes in the given tree having varlevelsup >= min_sublevels_up, * and add delta_sublevels_up to their varlevelsup value. This is needed when * an expression that's correct for some nesting level is inserted into a * subquery. Ordinarily the initial call has min_sublevels_up == 0 so that * all Vars are affected. The point of min_sublevels_up is that we can * increment it when we recurse into a sublink, so that local variables in * that sublink are not affected, only outer references to vars that belong * to the expression's original query level or parents thereof. * * Aggref nodes 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 delta_sublevels_up; int min_sublevels_up;} IncrementVarSublevelsUp_context;static boolIncrementVarSublevelsUp_walker(Node *node, IncrementVarSublevelsUp_context *context){ if (node == NULL) return false; if (IsA(node, Var)) { Var *var = (Var *) node; if (var->varlevelsup >= context->min_sublevels_up) var->varlevelsup += context->delta_sublevels_up; return false; /* done here */ } if (IsA(node, Aggref)) { Aggref *agg = (Aggref *) node; if (agg->agglevelsup >= context->min_sublevels_up) agg->agglevelsup += context->delta_sublevels_up; /* fall through to recurse into argument */ } if (IsA(node, Query)) { /* Recurse into subselects */ bool result; context->min_sublevels_up++; result = query_tree_walker((Query *) node, IncrementVarSublevelsUp_walker, (void *) context, 0); context->min_sublevels_up--; return result; } return expression_tree_walker(node, IncrementVarSublevelsUp_walker, (void *) context);}voidIncrementVarSublevelsUp(Node *node, int delta_sublevels_up, int min_sublevels_up){ IncrementVarSublevelsUp_context context; context.delta_sublevels_up = delta_sublevels_up; context.min_sublevels_up = min_sublevels_up; /* * Must be prepared to start with a Query or a bare expression tree; if * it's a Query, we don't want to increment sublevels_up. */ query_or_expression_tree_walker(node, IncrementVarSublevelsUp_walker, (void *) &context, 0);}/* * rangeTableEntry_used - detect whether an RTE is referenced somewhere * in var nodes or join or setOp trees of a query or expression.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -