PostgreSQL查询优化中怎么上拉子链接

70次阅读
没有评论

共计 43060 个字符,预计需要花费 108 分钟才能阅读完成。

本篇内容主要讲解“PostgreSQL 查询优化中怎么上拉子链接”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让丸趣 TV 小编来带大家学习“PostgreSQL 查询优化中怎么上拉子链接”吧!

查询树

convert_EXISTS_sublink_to_join 函数源码:

 /*
 * convert_EXISTS_sublink_to_join: try to convert an EXISTS SubLink to a join
 *
 * The API of this function is identical to convert_ANY_sublink_to_join s,
 * except that we also support the case where the caller has found NOT EXISTS,
 * so we need an additional input parameter  under_not .
 *  逻辑与 ANY 一致, 为了支持 NOT EXISTS, 多加了一个参数 under_not
 */
 
 JoinExpr *
 convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
 bool under_not, Relids available_rels)
 {
 JoinExpr *result;// 返回结果
 Query *parse = root- parse;// 查询树
 Query *subselect = (Query *) sublink- subselect;// 子查询
 Node *whereClause;//where 语句
 int rtoffset;//
 int varno;
 Relids clause_varnos;
 Relids upper_varnos;
 
 Assert(sublink- subLinkType == EXISTS_SUBLINK);
 
 /*
 * Can t flatten if it contains WITH. (We could arrange to pull up the
 * WITH into the parent query s cteList, but that risks changing the
 * semantics, since a WITH ought to be executed once per associated query
 * call.) Note that convert_ANY_sublink_to_join doesn t have to reject
 * this case, since it just produces a subquery RTE that doesn t have to
 * get flattened into the parent query.
 */
 if (subselect- cteList)// 存在 With 子句, 返回
 return NULL;
 
 /*
 * Copy the subquery so we can modify it safely (see comments in
 * make_subplan).
 */
 subselect = copyObject(subselect);
 
 /*
 * See if the subquery can be simplified based on the knowledge that it s
 * being used in EXISTS(). If we aren t able to get rid of its
 * targetlist, we have to fail, because the pullup operation leaves us
 * with noplace to evaluate the targetlist.
 */
 // 能否 handle targetList? 不行, 则退出
 // 如果含有集合操作 / 聚合操作 /Having 子句等, 子链接不能提升
 if (!simplify_EXISTS_query(root, subselect))
 return NULL;
 
 /*
 * The subquery must have a nonempty jointree, else we won t have a join.
 */
 if (subselect- jointree- fromlist == NIL)// 子查询没有查询主体, 退出
 return NULL;
 
 /*
 * Separate out the WHERE clause. (We could theoretically also remove
 * top-level plain JOIN/ON clauses, but it s probably not worth the
 * trouble.)
 */
 whereClause = subselect- jointree- quals;// 子查询条件语句单独保存
 subselect- jointree- quals = NULL;// 子查询的条件语句设置为 NULL
 
 /*
 * The rest of the sub-select must not refer to any Vars of the parent
 * query. (Vars of higher levels should be okay, though.)
 */
 if (contain_vars_of_level((Node *) subselect, 1))// 去掉条件语句后, 如仍依赖父查询的 Vars, 退出
 return NULL;
 
 /*
 * On the other hand, the WHERE clause must contain some Vars of the
 * parent query, else it s not gonna be a join.
 */
 if (!contain_vars_of_level(whereClause, 1))// 条件语句必须含有父查询的 Vars, 否则不构成连接, 退出
 return NULL;
 
 /*
 * We don t risk optimizing if the WHERE clause is volatile, either.
 */
 if (contain_volatile_functions(whereClause))// 条件语句存在易变函数(如随机函数等)
 return NULL;
 
 /*
 * Prepare to pull up the sub-select into top range table.
 *
 * We rely here on the assumption that the outer query has no references
 * to the inner (necessarily true). Therefore this is a lot easier than
 * what pull_up_subqueries has to go through.
 *
 * In fact, it s even easier than what convert_ANY_sublink_to_join has to
 * do. The machinations of simplify_EXISTS_query ensured that there is
 * nothing interesting in the subquery except an rtable and jointree, and
 * even the jointree FromExpr no longer has quals. So we can just append
 * the rtable to our own and use the FromExpr in our jointree. But first,
 * adjust all level-zero varnos in the subquery to account for the rtable
 * merger.
 */
 rtoffset = list_length(parse- rtable);// 获取 rtable 的长度(新 RTE 插入的偏移)
 // 调整子查询中 varno 为 0(指向 rtable)的 Vars,varno 调整为父查询 rtable 的 index
 //(详见依赖函数解析)
 OffsetVarNodes((Node *) subselect, rtoffset, 0);
 OffsetVarNodes(whereClause, rtoffset, 0);
 
 /*
 * Upper-level vars in subquery will now be one level closer to their
 * parent than before; in particular, anything that had been level 1
 * becomes level zero.
 */
 // 子查询中与父查询相关的 Vars,varlevelsup 需要从 Leve 1 变为 Level 0
 // 在子查询中, 这些 Vars 的 varlevelsup 为 1, 表示依赖于父查询 (上一层) 的 Vars, 提升后不存在此依赖, 需改为 0
 IncrementVarSublevelsUp((Node *) subselect, -1, 1);
 IncrementVarSublevelsUp(whereClause, -1, 1);
 
 /*
 * Now that the WHERE clause is adjusted to match the parent query
 * environment, we can easily identify all the level-zero rels it uses.
 * The ones  = rtoffset belong to the upper query; the ones   rtoffset do
 * not.
 */
 clause_varnos = pull_varnos(whereClause);
 upper_varnos = NULL;
 while ((varno = bms_first_member(clause_varnos))  = 0)
 { if (varno  = rtoffset)
 upper_varnos = bms_add_member(upper_varnos, varno);
 }
 bms_free(clause_varnos);
 Assert(!bms_is_empty(upper_varnos));
 
 /*
 * Now that we ve got the set of upper-level varnos, we can make the last
 * check: only available_rels can be referenced.
 */
 if (!bms_is_subset(upper_varnos, available_rels))
 return NULL;
 
 /* Now we can attach the modified subquery rtable to the parent */
 // 把子查询的 rtable 拼接到父查询的 rtable 中
 parse- rtable = list_concat(parse- rtable, subselect- rtable);
 
 // 构造 JoinExpr
 /*
 * And finally, build the JoinExpr node.
 */
 result = makeNode(JoinExpr);
 result- jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
 result- isNatural = false;
 result- larg = NULL; /* caller must fill this in */
 /* flatten out the FromExpr node if it s useless */
 if (list_length(subselect- jointree- fromlist) == 1)
 result- rarg = (Node *) linitial(subselect- jointree- fromlist);
 else
 result- rarg = (Node *) subselect- jointree;
 result- usingClause = NIL;
 result- quals = whereClause;
 result- alias = NULL;
 result- rtindex = 0; /* we don t need an RTE for it */
 
 return result;
 }

二、基础信息

相关数据结构
1、Var

 /*
 * Var - expression node representing a variable (ie, a table column)
 *
 * Note: during parsing/planning, varnoold/varoattno are always just copies
 * of varno/varattno. At the tail end of planning, Var nodes appearing in
 * upper-level plan nodes are reassigned to point to the outputs of their
 * subplans; for example, in a join node varno becomes INNER_VAR or OUTER_VAR
 * and varattno becomes the index of the proper element of that subplan s
 * target list. Similarly, INDEX_VAR is used to identify Vars that reference
 * an index column rather than a heap column. (In ForeignScan and CustomScan
 * plan nodes, INDEX_VAR is abused to signify references to columns of a
 * custom scan tuple type.) In all these cases, varnoold/varoattno hold the
 * original values. The code doesn t really need varnoold/varoattno, but they
 * are very useful for debugging and interpreting completed plans, so we keep
 * them around.
 */
 #define INNER_VAR 65000 /* reference to inner subplan */
 #define OUTER_VAR 65001 /* reference to outer subplan */
 #define INDEX_VAR 65002 /* reference to index column */
 
 #define IS_SPECIAL_VARNO(varno) ((varno)  = INNER_VAR)
 
 /* Symbols for the indexes of the special RTE entries in rules */
 #define PRS2_OLD_VARNO 1
 #define PRS2_NEW_VARNO 2
 
 typedef struct Var
 {
 Expr xpr;
 Index varno; /* index of this var s relation in the range
 * table, or INNER_VAR/OUTER_VAR/INDEX_VAR */
 AttrNumber varattno; /* attribute number of this var, or zero for
 * all attrs (whole-row Var) */
 Oid vartype; /* pg_type OID for the type of this var */
 int32 vartypmod; /* pg_attribute typmod value */
 Oid varcollid; /* OID of collation, or InvalidOid if none */
 Index varlevelsup; /* for subquery variables referencing outer
 * relations; 0 in a normal var,  0 means N
 * levels up */
 Index varnoold; /* original value of varno, for debugging */
 AttrNumber varoattno; /* original value of varattno */
 int location; /* token location, or -1 if unknown */
 } Var;

XX_one_pos

 /*
 * Lookup tables to avoid need for bit-by-bit groveling
 *
 * rightmost_one_pos[x] gives the bit number (0-7) of the rightmost one bit
 * in a nonzero byte value x. The entry for x=0 is never used.
 *
 * leftmost_one_pos[x] gives the bit number (0-7) of the leftmost one bit in a
 * nonzero byte value x. The entry for x=0 is never used.
 *
 * number_of_ones[x] gives the number of one-bits (0-8) in a byte value x.
 *
 * We could make these tables larger and reduce the number of iterations
 * in the functions that use them, but bytewise shifts and masks are
 * especially fast on many machines, so working a byte at a time seems best.
 */
 
 static const uint8 rightmost_one_pos[256] = {
 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
 };
 
 static const uint8 leftmost_one_pos[256] = {
 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
 };
 
 static const uint8 number_of_ones[256] = {
 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
 };

依赖的函数
simplify_EXISTS_query

 /*
 * simplify_EXISTS_query: remove any useless stuff in an EXISTS s subquery
 *
 * The only thing that matters about an EXISTS query is whether it returns
 * zero or more than zero rows. Therefore, we can remove certain SQL features
 * that won t affect that. The only part that is really likely to matter in
 * typical usage is simplifying the targetlist: it s a common habit to write
 *  SELECT * FROM  even though there is no need to evaluate any columns.
 *
 * Note: by suppressing the targetlist we could cause an observable behavioral
 * change, namely that any errors that might occur in evaluating the tlist
 * won t occur, nor will other side-effects of volatile functions. This seems
 * unlikely to bother anyone in practice.
 *
 * Returns true if was able to discard the targetlist, else false.
 */
 static bool
 simplify_EXISTS_query(PlannerInfo *root, Query *query)
 {
 /*
 * We don t try to simplify at all if the query uses set operations,
 * aggregates, grouping sets, SRFs, modifying CTEs, HAVING, OFFSET, or FOR
 * UPDATE/SHARE; none of these seem likely in normal usage and their
 * possible effects are complex. (Note: we could ignore an  OFFSET 0 
 * clause, but that traditionally is used as an optimization fence, so we
 * don t.)
 */
 if (query- commandType != CMD_SELECT ||
 query- setOperations ||
 query- hasAggs ||
 query- groupingSets ||
 query- hasWindowFuncs ||
 query- hasTargetSRFs ||
 query- hasModifyingCTE ||
 query- havingQual ||
 query- limitOffset ||
 query- rowMarks)
 return false;
 
 /*
 * LIMIT with a constant positive (or NULL) value doesn t affect the
 * semantics of EXISTS, so let s ignore such clauses. This is worth doing
 * because people accustomed to certain other DBMSes may be in the habit
 * of writing EXISTS(SELECT ... LIMIT 1) as an optimization. If there s a
 * LIMIT with anything else as argument, though, we can t simplify.
 */
 if (query- limitCount)
 {
 /*
 * The LIMIT clause has not yet been through eval_const_expressions,
 * so we have to apply that here. It might seem like this is a waste
 * of cycles, since the only case plausibly worth worrying about is
 *  LIMIT 1  ... but what we ll actually see is  LIMIT int8(1::int4) ,
 * so we have to fold constants or we re not going to recognize it.
 */
 Node *node = eval_const_expressions(root, query- limitCount);
 Const *limit;
 
 /* Might as well update the query if we simplified the clause. */
 query- limitCount = node;
 
 if (!IsA(node, Const))
 return false;
 
 limit = (Const *) node;
 Assert(limit- consttype == INT8OID);
 if (!limit- constisnull   DatumGetInt64(limit- constvalue)  = 0)
 return false;
 
 /* Whether or not the targetlist is safe, we can drop the LIMIT. */
 query- limitCount = NULL;
 }
 
 /*
 * Otherwise, we can throw away the targetlist, as well as any GROUP,
 * WINDOW, DISTINCT, and ORDER BY clauses; none of those clauses will
 * change a nonzero-rows result to zero rows or vice versa. (Furthermore,
 * since our parsetree representation of these clauses depends on the
 * targetlist, we d better throw them away if we drop the targetlist.)
 */
 query- targetList = NIL;
 query- groupClause = NIL;
 query- windowClause = NIL;
 query- distinctClause = NIL;
 query- sortClause = NIL;
 query- hasDistinctOn = false;
 
 return true;
 }

OffsetVarNodes

 void
 OffsetVarNodes(Node *node, int offset, int sublevels_up)
 {
 OffsetVarNodes_context context;// 上下文
 
 context.offset = 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, exclRelIndex 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;
 
 if (qry- onConflict   qry- onConflict- exclRelIndex)
 qry- onConflict- exclRelIndex += offset;
 
 foreach(l, qry- rowMarks)
 { RowMarkClause *rc = (RowMarkClause *) lfirst(l);
 
 rc- rti += offset;
 }
 }
 query_tree_walker(qry, OffsetVarNodes_walker,
 (void *)  context, 0);
 }
 else
 OffsetVarNodes_walker(node,  context);
 }

IncrementVarSublevelsUp

 void
 IncrementVarSublevelsUp(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,
 QTW_EXAMINE_RTES);
 }

IncrementVarSublevelsUp

 /*
 * 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.
 *
 * Likewise for other nodes containing levelsup fields, such as Aggref.
 *
 * 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 bool
 IncrementVarSublevelsUp_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, CurrentOfExpr))
 {
 /* this should not happen */
 if (context- min_sublevels_up == 0)
 elog(ERROR,  cannot push down CurrentOfExpr 
 return false;
 }
 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, GroupingFunc))
 { GroupingFunc *grp = (GroupingFunc *) node;
 
 if (grp- agglevelsup  = context- min_sublevels_up)
 grp- agglevelsup += context- delta_sublevels_up;
 /* fall through to recurse into argument */
 }
 if (IsA(node, PlaceHolderVar))
 { PlaceHolderVar *phv = (PlaceHolderVar *) node;
 
 if (phv- phlevelsup  = context- min_sublevels_up)
 phv- phlevelsup += context- delta_sublevels_up;
 /* fall through to recurse into argument */
 }
 if (IsA(node, RangeTblEntry))
 { RangeTblEntry *rte = (RangeTblEntry *) node;
 
 if (rte- rtekind == RTE_CTE)
 { if (rte- ctelevelsup  = context- min_sublevels_up)
 rte- ctelevelsup += context- delta_sublevels_up;
 }
 return false; /* allow range_table_walker to continue */
 }
 if (IsA(node, Query))
 {
 /* Recurse into subselects */
 bool result;
 
 context- min_sublevels_up++;
 result = query_tree_walker((Query *) node,
 IncrementVarSublevelsUp_walker,
 (void *) context,
 QTW_EXAMINE_RTES);
 context- min_sublevels_up--;
 return result;
 }
 return expression_tree_walker(node, IncrementVarSublevelsUp_walker,
 (void *) context);
 }

pull_varnos

/*
 * pull_varnos
 * Create a set of all the distinct varnos present in a parsetree.
 * Only varnos that reference level-zero rtable entries are considered.
 *
 * NOTE: this is used on not-yet-planned expressions. It may therefore find
 * bare SubLinks, and if so it needs to recurse into them to look for uplevel
 * references to the desired rtable level! But when we find a completed
 * SubPlan, we only need to look at the parameters passed to the subplan.
 */
 Relids
 pull_varnos(Node *node)
 {
 pull_varnos_context context;
 
 context.varnos = NULL;
 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.
 */
 query_or_expression_tree_walker(node,
 pull_varnos_walker,
 (void *)  context,
 0);
 
 return context.varnos;
 }

contain_vars_of_level

/*
 * contain_vars_of_level
 * Recursively scan a clause to discover whether it contains any Var nodes
 * of the specified query level.
 *
 * Returns true if any such Var found.
 *
 * Will recurse into sublinks. Also, may be invoked directly on a Query.
 */
 bool
 contain_vars_of_level(Node *node, int levelsup)
 {
 int sublevels_up = levelsup;
 
 return query_or_expression_tree_walker(node,
 contain_vars_of_level_walker,
 (void *)  sublevels_up,
 0);
 }

query_tree_walker

 
 /*
 * query_tree_walker --- initiate a walk of a Query s expressions
 *
 * This routine exists just to reduce the number of places that need to know
 * where all the expression subtrees of a Query are. Note it can be used
 * for starting a walk at top level of a Query regardless of whether the
 * walker intends to descend into subqueries. It is also useful for
 * descending into subqueries within a walker.
 *
 * Some callers want to suppress visitation of certain items in the sub-Query,
 * typically because they need to process them specially, or don t actually
 * want to recurse into subqueries. This is supported by the flags argument,
 * which is the bitwise OR of flag values to suppress visitation of
 * indicated items. (More flag bits may be added as needed.)
 */
 bool
 query_tree_walker(Query *query,
 bool (*walker) (),
 void *context,
 int flags)
 { Assert(query != NULL   IsA(query, Query));
 
 if (walker((Node *) query- targetList, context))
 return true;
 if (walker((Node *) query- withCheckOptions, context))
 return true;
 if (walker((Node *) query- onConflict, context))
 return true;
 if (walker((Node *) query- returningList, context))
 return true;
 if (walker((Node *) query- jointree, context))
 return true;
 if (walker(query- setOperations, context))
 return true;
 if (walker(query- havingQual, context))
 return true;
 if (walker(query- limitOffset, context))
 return true;
 if (walker(query- limitCount, context))
 return true;
 if (!(flags   QTW_IGNORE_CTE_SUBQUERIES))
 { if (walker((Node *) query- cteList, context))
 return true;
 }
 if (!(flags   QTW_IGNORE_RANGE_TABLE))
 { if (range_table_walker(query- rtable, walker, context, flags))
 return true;
 }
 return false;
 }

OffsetVarNodes_walker

 /*
 * 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 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, CurrentOfExpr))
 { CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
 
 if (context- sublevels_up == 0)
 cexpr- cvarno += 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 (j- rtindex   context- sublevels_up == 0)
 j- rtindex += context- offset;
 /* fall through to examine children */
 }
 if (IsA(node, PlaceHolderVar))
 { PlaceHolderVar *phv = (PlaceHolderVar *) node;
 
 if (phv- phlevelsup == context- sublevels_up)
 {
 phv- phrels = offset_relid_set(phv- phrels,
 context- offset);
 }
 /* fall through to examine children */
 }
 if (IsA(node, AppendRelInfo))
 { AppendRelInfo *appinfo = (AppendRelInfo *) node;
 
 if (context- sublevels_up == 0)
 {
 appinfo- parent_relid += context- offset;
 appinfo- child_relid += context- offset;
 }
 /* fall through to examine children */
 }
 /* Shouldn t need to handle other planner auxiliary nodes here */
 Assert(!IsA(node, PlanRowMark));
 Assert(!IsA(node, SpecialJoinInfo));
 Assert(!IsA(node, PlaceHolderInfo));
 Assert(!IsA(node, MinMaxAggInfo));
 
 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);
 }

query_or_expression_tree_walker

 /*
 * query_or_expression_tree_walker --- hybrid form
 *
 * This routine will invoke query_tree_walker if called on a Query node,
 * else will invoke the walker directly. This is a useful way of starting
 * the recursion when the walker s normal change of state is not appropriate
 * for the outermost Query node.
 */
 bool
 query_or_expression_tree_walker(Node *node,
 bool (*walker) (),
 void *context,
 int flags)
 { if (node   IsA(node, Query))
 return query_tree_walker((Query *) node,
 walker,
 context,
 flags);
 else
 return walker(node, context);
 }

pull_varnos_walker

 static bool
 pull_varnos_walker(Node *node, pull_varnos_context *context)
 { if (node == NULL)
 return false;
 if (IsA(node, Var))
 { Var *var = (Var *) node;
 
 if (var- varlevelsup == context- sublevels_up)
 context- varnos = bms_add_member(context- varnos, var- varno);
 return false;
 }
 if (IsA(node, CurrentOfExpr))
 { CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
 
 if (context- sublevels_up == 0)
 context- varnos = bms_add_member(context- varnos, cexpr- cvarno);
 return false;
 }
 if (IsA(node, PlaceHolderVar))
 {
 /*
 * A PlaceHolderVar acts as a variable of its syntactic scope, or
 * lower than that if it references only a subset of the rels in its
 * syntactic scope. It might also contain lateral references, but we
 * should ignore such references when computing the set of varnos in
 * an expression tree. Also, if the PHV contains no variables within
 * its syntactic scope, it will be forced to be evaluated exactly at
 * the syntactic scope, so take that as the relid set.
 */
 PlaceHolderVar *phv = (PlaceHolderVar *) node;
 pull_varnos_context subcontext;
 
 subcontext.varnos = NULL;
 subcontext.sublevels_up = context- sublevels_up;
 (void) pull_varnos_walker((Node *) phv- phexpr,  subcontext);
 if (phv- phlevelsup == context- sublevels_up)
 {
 subcontext.varnos = bms_int_members(subcontext.varnos,
 phv- phrels);
 if (bms_is_empty(subcontext.varnos))
 context- varnos = bms_add_members(context- varnos,
 phv- phrels);
 }
 context- varnos = bms_join(context- varnos, subcontext.varnos);
 return false;
 }
 if (IsA(node, Query))
 {
 /* Recurse into RTE subquery or not-yet-planned sublink subquery */
 bool result;
 
 context- sublevels_up++;
 result = query_tree_walker((Query *) node, pull_varnos_walker,
 (void *) context, 0);
 context- sublevels_up--;
 return result;
 }
 return expression_tree_walker(node, pull_varnos_walker,
 (void *) context);
 }

contain_vars_of_level_walker

 static bool
 contain_vars_of_level_walker(Node *node, int *sublevels_up)
 { if (node == NULL)
 return false;
 if (IsA(node, Var))
 { if (((Var *) node)- varlevelsup == *sublevels_up)
 return true; /* abort tree traversal and return true */
 return false;
 }
 if (IsA(node, CurrentOfExpr))
 { if (*sublevels_up == 0)
 return true;
 return false;
 }
 if (IsA(node, PlaceHolderVar))
 { if (((PlaceHolderVar *) node)- phlevelsup == *sublevels_up)
 return true; /* abort the tree traversal and return true */
 /* else fall through to check the contained expr */
 }
 if (IsA(node, Query))
 {
 /* Recurse into subselects */
 bool result;
 
 (*sublevels_up)++;
 result = query_tree_walker((Query *) node,
 contain_vars_of_level_walker,
 (void *) sublevels_up,
 0);
 (*sublevels_up)--;
 return result;
 }
 return expression_tree_walker(node,
 contain_vars_of_level_walker,
 (void *) sublevels_up);
 }

expression_tree_walker

 /*
 * Standard expression-tree walking support
 *
 * We used to have near-duplicate code in many different routines that
 * understood how to recurse through an expression node tree. That was
 * a pain to maintain, and we frequently had bugs due to some particular
 * routine neglecting to support a particular node type. In most cases,
 * these routines only actually care about certain node types, and don t
 * care about other types except insofar as they have to recurse through
 * non-primitive node types. Therefore, we now provide generic tree-walking
 * logic to consolidate the redundant  boilerplate  code. There are
 * two versions: expression_tree_walker() and expression_tree_mutator().
 */
 
 /*
 * expression_tree_walker() is designed to support routines that traverse
 * a tree in a read-only fashion (although it will also work for routines
 * that modify nodes in-place but never add/delete/replace nodes).
 * A walker routine should look like this:
 *
 * bool my_walker (Node *node, my_struct *context)
 * { * if (node == NULL)
 * return false;
 * // check for nodes that special work is required for, eg:
 * if (IsA(node, Var))
 * {
 * ... do special actions for Var nodes
 * }
 * else if (IsA(node, ...))
 * {
 * ... do special actions for other node types
 * }
 * // for any node type not specially processed, do:
 * return expression_tree_walker(node, my_walker, (void *) context);
 * }
 *
 * The  context  argument points to a struct that holds whatever context
 * information the walker routine needs --- it can be used to return data
 * gathered by the walker, too. This argument is not touched by
 * expression_tree_walker, but it is passed down to recursive sub-invocations
 * of my_walker. The tree walk is started from a setup routine that
 * fills in the appropriate context struct, calls my_walker with the top-level
 * node of the tree, and then examines the results.
 *
 * The walker routine should return  false  to continue the tree walk, or
 *  true  to abort the walk and immediately return  true  to the top-level
 * caller. This can be used to short-circuit the traversal if the walker
 * has found what it came for.  false  is returned to the top-level caller
 * iff no invocation of the walker returned  true .
 *
 * The node types handled by expression_tree_walker include all those
 * normally found in target lists and qualifier clauses during the planning
 * stage. In particular, it handles List nodes since a cnf-ified qual clause
 * will have List structure at the top level, and it handles TargetEntry nodes
 * so that a scan of a target list can be handled without additional code.
 * Also, RangeTblRef, FromExpr, JoinExpr, and SetOperationStmt nodes are
 * handled, so that query jointrees and setOperation trees can be processed
 * without additional code.
 *
 * expression_tree_walker will handle SubLink nodes by recursing normally
 * into the  testexpr  subtree (which is an expression belonging to the outer
 * plan). It will also call the walker on the sub-Query node; however, when
 * expression_tree_walker itself is called on a Query node, it does nothing
 * and returns  false . The net effect is that unless the walker does
 * something special at a Query node, sub-selects will not be visited during
 * an expression tree walk. This is exactly the behavior wanted in many cases
 * --- and for those walkers that do want to recurse into sub-selects, special
 * behavior is typically needed anyway at the entry to a sub-select (such as
 * incrementing a depth counter). A walker that wants to examine sub-selects
 * should include code along the lines of:
 *
 * if (IsA(node, Query))
 * {
 * adjust context for subquery;
 * result = query_tree_walker((Query *) node, my_walker, context,
 * 0); // adjust flags as needed
 * restore context if needed;
 * return result;
 * }
 *
 * query_tree_walker is a convenience routine (see below) that calls the
 * walker on all the expression subtrees of the given Query node.
 *
 * expression_tree_walker will handle SubPlan nodes by recursing normally
 * into the  testexpr  and the  args  list (which are expressions belonging to
 * the outer plan). It will not touch the completed subplan, however. Since
 * there is no link to the original Query, it is not possible to recurse into
 * subselects of an already-planned expression tree. This is OK for current
 * uses, but may need to be revisited in future.
 */
 
 bool
 expression_tree_walker(Node *node,
 bool (*walker) (),
 void *context)
 {
 ListCell *temp;
 
 /*
 * The walker has already visited the current node, and so we need only
 * recurse into any sub-nodes it has.
 *
 * We assume that the walker is not interested in List nodes per se, so
 * when we expect a List we just recurse directly to self without
 * bothering to call the walker.
 */
 if (node == NULL)
 return false;
 
 /* Guard against stack overflow due to overly complex expressions */
 check_stack_depth();
 
 switch (nodeTag(node))
 {
 case T_Var:
 case T_Const:
 case T_Param:
 case T_CaseTestExpr:
 case T_SQLValueFunction:
 case T_CoerceToDomainValue:
 case T_SetToDefault:
 case T_CurrentOfExpr:
 case T_NextValueExpr:
 case T_RangeTblRef:
 case T_SortGroupClause:
 /* primitive node types with no expression subnodes */
 break;
 case T_WithCheckOption:
 return walker(((WithCheckOption *) node)- qual, context);
 case T_Aggref:
 { Aggref *expr = (Aggref *) node;
 
 /* recurse directly on List */
 if (expression_tree_walker((Node *) expr- aggdirectargs,
 walker, context))
 return true;
 if (expression_tree_walker((Node *) expr- args,
 walker, context))
 return true;
 if (expression_tree_walker((Node *) expr- aggorder,
 walker, context))
 return true;
 if (expression_tree_walker((Node *) expr- aggdistinct,
 walker, context))
 return true;
 if (walker((Node *) expr- aggfilter, context))
 return true;
 }
 break;
 case T_GroupingFunc:
 { GroupingFunc *grouping = (GroupingFunc *) node;
 
 if (expression_tree_walker((Node *) grouping- args,
 walker, context))
 return true;
 }
 break;
 case T_WindowFunc:
 { WindowFunc *expr = (WindowFunc *) node;
 
 /* recurse directly on List */
 if (expression_tree_walker((Node *) expr- args,
 walker, context))
 return true;
 if (walker((Node *) expr- aggfilter, context))
 return true;
 }
 break;
 case T_ArrayRef:
 { ArrayRef *aref = (ArrayRef *) node;
 
 /* recurse directly for upper/lower array index lists */
 if (expression_tree_walker((Node *) aref- refupperindexpr,
 walker, context))
 return true;
 if (expression_tree_walker((Node *) aref- reflowerindexpr,
 walker, context))
 return true;
 /* walker must see the refexpr and refassgnexpr, however */
 if (walker(aref- refexpr, context))
 return true;
 if (walker(aref- refassgnexpr, context))
 return true;
 }
 break;
 case T_FuncExpr:
 { FuncExpr *expr = (FuncExpr *) node;
 
 if (expression_tree_walker((Node *) expr- args,
 walker, context))
 return true;
 }
 break;
 case T_NamedArgExpr:
 return walker(((NamedArgExpr *) node)- arg, context);
 case T_OpExpr:
 case T_DistinctExpr: /* struct-equivalent to OpExpr */
 case T_NullIfExpr: /* struct-equivalent to OpExpr */
 { OpExpr *expr = (OpExpr *) node;
 
 if (expression_tree_walker((Node *) expr- args,
 walker, context))
 return true;
 }
 break;
 case T_ScalarArrayOpExpr:
 { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
 
 if (expression_tree_walker((Node *) expr- args,
 walker, context))
 return true;
 }
 break;
 case T_BoolExpr:
 { BoolExpr *expr = (BoolExpr *) node;
 
 if (expression_tree_walker((Node *) expr- args,
 walker, context))
 return true;
 }
 break;
 case T_SubLink:
 { SubLink *sublink = (SubLink *) node;
 
 if (walker(sublink- testexpr, context))
 return true;
 
 /*
 * Also invoke the walker on the sublink s Query node, so it
 * can recurse into the sub-query if it wants to.
 */
 return walker(sublink- subselect, context);
 }
 break;
 case T_SubPlan:
 { SubPlan *subplan = (SubPlan *) node;
 
 /* recurse into the testexpr, but not into the Plan */
 if (walker(subplan- testexpr, context))
 return true;
 /* also examine args list */
 if (expression_tree_walker((Node *) subplan- args,
 walker, context))
 return true;
 }
 break;
 case T_AlternativeSubPlan:
 return walker(((AlternativeSubPlan *) node)- subplans, context);
 case T_FieldSelect:
 return walker(((FieldSelect *) node)- arg, context);
 case T_FieldStore:
 { FieldStore *fstore = (FieldStore *) node;
 
 if (walker(fstore- arg, context))
 return true;
 if (walker(fstore- newvals, context))
 return true;
 }
 break;
 case T_RelabelType:
 return walker(((RelabelType *) node)- arg, context);
 case T_CoerceViaIO:
 return walker(((CoerceViaIO *) node)- arg, context);
 case T_ArrayCoerceExpr:
 { ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
 
 if (walker(acoerce- arg, context))
 return true;
 if (walker(acoerce- elemexpr, context))
 return true;
 }
 break;
 case T_ConvertRowtypeExpr:
 return walker(((ConvertRowtypeExpr *) node)- arg, context);
 case T_CollateExpr:
 return walker(((CollateExpr *) node)- arg, context);
 case T_CaseExpr:
 { CaseExpr *caseexpr = (CaseExpr *) node;
 
 if (walker(caseexpr- arg, context))
 return true;
 /* we assume walker doesn t care about CaseWhens, either */
 foreach(temp, caseexpr- args)
 { CaseWhen *when = lfirst_node(CaseWhen, temp);
 
 if (walker(when- expr, context))
 return true;
 if (walker(when- result, context))
 return true;
 }
 if (walker(caseexpr- defresult, context))
 return true;
 }
 break;
 case T_ArrayExpr:
 return walker(((ArrayExpr *) node)- elements, context);
 case T_RowExpr:
 /* Assume colnames isn t interesting */
 return walker(((RowExpr *) node)- args, context);
 case T_RowCompareExpr:
 { RowCompareExpr *rcexpr = (RowCompareExpr *) node;
 
 if (walker(rcexpr- largs, context))
 return true;
 if (walker(rcexpr- rargs, context))
 return true;
 }
 break;
 case T_CoalesceExpr:
 return walker(((CoalesceExpr *) node)- args, context);
 case T_MinMaxExpr:
 return walker(((MinMaxExpr *) node)- args, context);
 case T_XmlExpr:
 { XmlExpr *xexpr = (XmlExpr *) node;
 
 if (walker(xexpr- named_args, context))
 return true;
 /* we assume walker doesn t care about arg_names */
 if (walker(xexpr- args, context))
 return true;
 }
 break;
 case T_NullTest:
 return walker(((NullTest *) node)- arg, context);
 case T_BooleanTest:
 return walker(((BooleanTest *) node)- arg, context);
 case T_CoerceToDomain:
 return walker(((CoerceToDomain *) node)- arg, context);
 case T_TargetEntry:
 return walker(((TargetEntry *) node)- expr, context);
 case T_Query:
 /* Do nothing with a sub-Query, per discussion above */
 break;
 case T_WindowClause:
 { WindowClause *wc = (WindowClause *) node;
 
 if (walker(wc- partitionClause, context))
 return true;
 if (walker(wc- orderClause, context))
 return true;
 if (walker(wc- startOffset, context))
 return true;
 if (walker(wc- endOffset, context))
 return true;
 }
 break;
 case T_CommonTableExpr:
 { CommonTableExpr *cte = (CommonTableExpr *) node;
 
 /*
 * Invoke the walker on the CTE s Query node, so it can
 * recurse into the sub-query if it wants to.
 */
 return walker(cte- ctequery, context);
 }
 break;
 case T_List:
 foreach(temp, (List *) node)
 { if (walker((Node *) lfirst(temp), context))
 return true;
 }
 break;
 case T_FromExpr:
 { FromExpr *from = (FromExpr *) node;
 
 if (walker(from- fromlist, context))
 return true;
 if (walker(from- quals, context))
 return true;
 }
 break;
 case T_OnConflictExpr:
 { OnConflictExpr *onconflict = (OnConflictExpr *) node;
 
 if (walker((Node *) onconflict- arbiterElems, context))
 return true;
 if (walker(onconflict- arbiterWhere, context))
 return true;
 if (walker(onconflict- onConflictSet, context))
 return true;
 if (walker(onconflict- onConflictWhere, context))
 return true;
 if (walker(onconflict- exclRelTlist, context))
 return true;
 }
 break;
 case T_PartitionPruneStepOp:
 { PartitionPruneStepOp *opstep = (PartitionPruneStepOp *) node;
 
 if (walker((Node *) opstep- exprs, context))
 return true;
 }
 break;
 case T_PartitionPruneStepCombine:
 /* no expression subnodes */
 break;
 case T_JoinExpr:
 { JoinExpr *join = (JoinExpr *) node;
 
 if (walker(join- larg, context))
 return true;
 if (walker(join- rarg, context))
 return true;
 if (walker(join- quals, context))
 return true;
 
 /*
 * alias clause, using list are deemed uninteresting.
 */
 }
 break;
 case T_SetOperationStmt:
 { SetOperationStmt *setop = (SetOperationStmt *) node;
 
 if (walker(setop- larg, context))
 return true;
 if (walker(setop- rarg, context))
 return true;
 
 /* groupClauses are deemed uninteresting */
 }
 break;
 case T_PlaceHolderVar:
 return walker(((PlaceHolderVar *) node)- phexpr, context);
 case T_InferenceElem:
 return walker(((InferenceElem *) node)- expr, context);
 case T_AppendRelInfo:
 { AppendRelInfo *appinfo = (AppendRelInfo *) node;
 
 if (expression_tree_walker((Node *) appinfo- translated_vars,
 walker, context))
 return true;
 }
 break;
 case T_PlaceHolderInfo:
 return walker(((PlaceHolderInfo *) node)- ph_var, context);
 case T_RangeTblFunction:
 return walker(((RangeTblFunction *) node)- funcexpr, context);
 case T_TableSampleClause:
 { TableSampleClause *tsc = (TableSampleClause *) node;
 
 if (expression_tree_walker((Node *) tsc- args,
 walker, context))
 return true;
 if (walker((Node *) tsc- repeatable, context))
 return true;
 }
 break;
 case T_TableFunc:
 { TableFunc *tf = (TableFunc *) node;
 
 if (walker(tf- ns_uris, context))
 return true;
 if (walker(tf- docexpr, context))
 return true;
 if (walker(tf- rowexpr, context))
 return true;
 if (walker(tf- colexprs, context))
 return true;
 if (walker(tf- coldefexprs, context))
 return true;
 }
 break;
 default:
 elog(ERROR,  unrecognized node type: %d ,
 (int) nodeTag(node));
 break;
 }
 return false;
 }

三、跟踪分析

测试脚本:

select * 
from t_dwxx a
where exists (select b.dwbh from t_grxx b where a.dwbh = b.dwbh);

gdb 分析:

(gdb) b convert_EXISTS_sublink_to_join
Breakpoint 1 at 0x77a4fe: file subselect.c, line 1426.
(gdb) c
Continuing.
Breakpoint 1, convert_EXISTS_sublink_to_join (root=0x22de828, sublink=0x22292a0, under_not=false, available_rels=0x22defa8)
 at subselect.c:1426
1426 Query *parse = root- parse;
(gdb) 
#1.root 见上一节
#2.sublink, 子链接, 见本节查询树结构
#3.under_not,false, 表示 EXISTS
#4.available_rels, 可用的 rels
(gdb) p *available_rels
$1 = {nwords = 1, words = 0x22defac}
(gdb) p available_rels- words[0]
$3 = 2
(gdb) 
1511 rtoffset = list_length(parse- rtable);
(gdb) 
1512 OffsetVarNodes((Node *) subselect, rtoffset, 0);
(gdb) p rtoffset
$6 = 1
(gdb) step
OffsetVarNodes (node=0x22dee48, offset=1, sublevels_up=0) at rewriteManip.c:428
428 context.offset = offset;
#调整 subselect- jointree- fromlist- head(类型为 RTR)的 rtindex, 原为 1, 调整为 2
(gdb) p *node
$20 = {type = T_RangeTblRef}
(gdb) p *(RangeTblRef *)node
$21 = {type = T_RangeTblRef, rtindex = 1}
(gdb) n
368 rtr- rtindex += context- offset;
(gdb) 
370 return false;
(gdb) p *(RangeTblRef *)node
$22 = {type = T_RangeTblRef, rtindex = 2}
(gdb) 
1513 OffsetVarNodes(whereClause, rtoffset, 0);
(gdb) 
1520 IncrementVarSublevelsUp((Node *) subselect, -1, 1);
(gdb) p whereClause
$23 = (Node *) 0x22def58
(gdb) p *whereClause
$24 = {type = T_OpExpr}
(gdb) set $arg1=(RelabelType *)((OpExpr *)whereClause)- args- head- data.ptr_value
(gdb) p *$arg1- arg
$32 = {type = T_Var}
#第 1 个参数是 t_dwxx.dwbh
(gdb) p *(Var *)$arg1- arg
$33 = {xpr = {type = T_Var}, varno = 1, varattno = 2, vartype = 1043, vartypmod = 14, varcollid = 100, varlevelsup = 1, 
 varnoold = 1, varoattno = 2, location = 72}
(gdb) set $arg2=(RelabelType *)((OpExpr *)whereClause)- args- tail- data.ptr_value
#第 2 个参数是 t_grxx.dwbh
#varno/varnoold 从原来的值 1 调整为 2
(gdb) p *(Var *)$arg2- arg
$34 = {xpr = {type = T_Var}, varno = 2, varattno = 1, vartype = 1043, vartypmod = 14, varcollid = 100, varlevelsup = 0, 
 varnoold = 2, varoattno = 1, location = 81}
(gdb) 
(gdb) n
1521 IncrementVarSublevelsUp(whereClause, -1, 1);
(gdb) 
1529 clause_varnos = pull_varnos(whereClause);
#调整 varlevelsup 为 0
(gdb) p *(Var *)$arg1- arg
$36 = {xpr = {type = T_Var}, varno = 1, varattno = 2, vartype = 1043, vartypmod = 14, varcollid = 100, varlevelsup = 0, 
 varnoold = 1, varoattno = 2, location = 72}
(gdb) p *(Var *)$arg2- arg
$37 = {xpr = {type = T_Var}, varno = 2, varattno = 1, vartype = 1043, vartypmod = 14, varcollid = 100, varlevelsup = 0, 
 varnoold = 2, varoattno = 1, location = 81}
#构造返回值
(gdb)
1552 result = makeNode(JoinExpr);
1566 return result;
(gdb) 
1567 }
(gdb) 
pull_up_sublinks_qual_recurse (root=0x22de828, node=0x22292a0, jtlink1=0x7ffdcabbc918, available_rels1=0x22defa8, 
 jtlink2=0x0, available_rels2=0x0) at prepjointree.c:404
404 j- larg = *jtlink1;
#上拉成为半连接 SEMI JOIN
(gdb) p *(JoinExpr *)jtnode
$65 = {type = T_JoinExpr, jointype = JOIN_SEMI, isNatural = false, larg = 0x22e7118, rarg = 0x22e7438, usingClause = 0x0, 
 quals = 0x22def58, alias = 0x0, rtindex = 0}
#DONE!

到此,相信大家对“PostgreSQL 查询优化中怎么上拉子链接”有了更深的了解,不妨来实际操作一番吧!这里是丸趣 TV 网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-20发表,共计43060字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)