千家信息网

PostgreSQL如何实现上拉子链接

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章主要介绍了PostgreSQL如何实现上拉子链接,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。按官方文档的介绍,子链接Subl
千家信息网最后更新 2026年11月01日PostgreSQL如何实现上拉子链接

这篇文章主要介绍了PostgreSQL如何实现上拉子链接,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

按官方文档的介绍,子链接Sublink代表的是出现在表达式(可能会出现组合运算符)中的子查询,子查询的类型包括:
EXISTS_SUBLINK
语法:EXISTS(SELECT ...)

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

ALL_SUBLINK
语法:(lefthand) op ALL (SELECT ...)

select * from t_dwxx awhere dwbh > all (select b.dwbh from t_grxx b);

ANY_SUBLINK
语法:(lefthand) op ANY (SELECT ...)

select * from t_dwxx awhere dwbh = any (select b.dwbh from t_grxx b);

ROWCOMPARE_SUBLINK
语法:(lefthand) op (SELECT ...)

select * from t_dwxx awhere dwbh > (select max(b.dwbh) from t_grxx b);

EXPR_SUBLINK
语法:(SELECT with single targetlist item ...)

select *,(select max(dwbh) from t_grxx) from t_dwxx a;

MULTIEXPR_SUBLINK
语法:(SELECT with multiple targetlist items ...)

ARRAY_SUBLINK
语法:ARRAY(SELECT with single targetlist item ...)

CTE_SUBLINK
语法:
WITH query (never actually part of an expression)

官方说明:

/* * SubLink * * A SubLink represents a subselect appearing in an expression, and in some * cases also the combining operator(s) just above it.  The subLinkType * indicates the form of the expression represented: *  EXISTS_SUBLINK    EXISTS(SELECT ...) *  ALL_SUBLINK     (lefthand) op ALL (SELECT ...) *  ANY_SUBLINK     (lefthand) op ANY (SELECT ...) *  ROWCOMPARE_SUBLINK  (lefthand) op (SELECT ...) *  EXPR_SUBLINK    (SELECT with single targetlist item ...) *  MULTIEXPR_SUBLINK (SELECT with multiple targetlist items ...) *  ARRAY_SUBLINK   ARRAY(SELECT with single targetlist item ...) *  CTE_SUBLINK     WITH query (never actually part of an expression) * For ALL, ANY, and ROWCOMPARE, the lefthand is a list of expressions of the * same length as the subselect's targetlist.  ROWCOMPARE will *always* have * a list with more than one entry; if the subselect has just one target * then the parser will create an EXPR_SUBLINK instead (and any operator * above the subselect will be represented separately). * ROWCOMPARE, EXPR, and MULTIEXPR require the subselect to deliver at most * one row (if it returns no rows, the result is NULL). * ALL, ANY, and ROWCOMPARE require the combining operators to deliver boolean * results.  ALL and ANY combine the per-row results using AND and OR * semantics respectively. * ARRAY requires just one target column, and creates an array of the target * column's type using any number of rows resulting from the subselect. *  * 子链接属于Expr Node,但并不意味着子链接是可以执行的.子链接必须在计划期间通过子计划节点在表达式树中替换 * SubLink is classed as an Expr node, but it is not actually executable; * it must be replaced in the expression tree by a SubPlan node during * planning. * * NOTE: in the raw output of gram.y, testexpr contains just the raw form * of the lefthand _expression (if any), and operName is the String name of * the combining operator.  Also, subselect is a raw parsetree.  During parse * analysis, the parser transforms testexpr into a complete boolean expression * that compares the lefthand value(s) to PARAM_SUBLINK nodes representing the * output columns of the subselect.  And subselect is transformed to a Query. * This is the representation seen in saved rules and in the rewriter. * * In EXISTS, EXPR, MULTIEXPR, and ARRAY SubLinks, testexpr and operName * are unused and are always null. * * subLinkId is currently used only for MULTIEXPR SubLinks, and is zero in * other SubLinks.  This number identifies different multiple-assignment * subqueries within an UPDATE statement's SET list.  It is unique only * within a particular targetlist.  The output column(s) of the MULTIEXPR * are referenced by PARAM_MULTIEXPR Params appearing elsewhere in the tlist. * * The CTE_SUBLINK case never occurs in actual SubLink nodes, but it is used * in SubPlans generated for WITH subqueries. */typedef enum SubLinkType{  EXISTS_SUBLINK,  ALL_SUBLINK,  ANY_SUBLINK,  ROWCOMPARE_SUBLINK,  EXPR_SUBLINK,  MULTIEXPR_SUBLINK,  ARRAY_SUBLINK,  CTE_SUBLINK         /* for SubPlans only */} SubLinkType;typedef struct SubLink{  Expr    xpr;  SubLinkType subLinkType;  /* see above */  int     subLinkId;    /* ID (1..n); 0 if not MULTIEXPR */  Node     *testexpr;   /* outer-query test for ALL/ANY/ROWCOMPARE */  List     *operName;   /* originally specified operator name */  Node     *subselect;    /* subselect as Query* or raw parsetree */  int     location;   /* token location, or -1 if unknown */} SubLink;

二、源码解读

standard_planner函数

/*****************************************************************************  *  *     Query optimizer entry point  *  * To support loadable plugins that monitor or modify planner behavior,  * we provide a hook variable that lets a plugin get control before and  * after the standard planning process.  The plugin would normally call  * standard_planner().  *  * Note to plugin authors: standard_planner() scribbles on its Query input,  * so you'd better copy that data structure if you want to plan more than once.  *  *****************************************************************************/ PlannedStmt * planner(Query *parse, int cursorOptions, ParamListInfo boundParams) {     PlannedStmt *result;      if (planner_hook)         result = (*planner_hook) (parse, cursorOptions, boundParams); //钩子函数,可以实现定制化开发     else         result = standard_planner(parse, cursorOptions, boundParams);//PG的标准实现     return result; }  /* 输入参数:    parse-查询树    cursorOptions-游标处理选项,见本节中的数据结构    boundParams-绑定变量? 输出参数: */ PlannedStmt * standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) {     PlannedStmt *result;//最终结果     PlannerGlobal *glob;//全局优化信息     double      tuple_fraction;//优化过程中元组的采样率     PlannerInfo *root;//执行计划的根节点     RelOptInfo *final_rel;//优化后的Relation信息     Path       *best_path;//最优路径     Plan       *top_plan;//顶层计划     ListCell   *lp,//临时变量                *lr;      /*      * Set up global state for this planner invocation.  This data is needed      * across all levels of sub-Query that might exist in the given command,      * so we keep it in a separate struct that's linked to by each per-Query      * PlannerInfo.      */     //初始化全局优化信息      glob = makeNode(PlannerGlobal);      glob->boundParams = boundParams;     glob->subplans = NIL;     glob->subroots = NIL;     glob->rewindPlanIDs = NULL;     glob->finalrtable = NIL;     glob->finalrowmarks = NIL;     glob->resultRelations = NIL;     glob->nonleafResultRelations = NIL;     glob->rootResultRelations = NIL;     glob->relationOids = NIL;     glob->invalItems = NIL;     glob->paramExecTypes = NIL;     glob->lastPHId = 0;     glob->lastRowMarkId = 0;     glob->lastPlanNodeId = 0;     glob->transientPlan = false;     glob->dependsOnRole = false;      /*      * Assess whether it's feasible to use parallel mode for this query. We      * can't do this in a standalone backend, or if the command will try to      * modify any data, or if this is a cursor operation, or if GUCs are set      * to values that don't permit parallelism, or if parallel-unsafe      * functions are present in the query tree.      *      * (Note that we do allow CREATE TABLE AS, SELECT INTO, and CREATE      * MATERIALIZED VIEW to use parallel plans, but this is safe only because      * the command is writing into a completely new table which workers won't      * be able to see.  If the workers could see the table, the fact that      * group locking would cause them to ignore the leader's heavyweight      * relation extension lock and GIN page locks would make this unsafe.      * We'll have to fix that somehow if we want to allow parallel inserts in      * general; updates and deletes have additional problems especially around      * combo CIDs.)      *      * For now, we don't try to use parallel mode if we're running inside a      * parallel worker.  We might eventually be able to relax this      * restriction, but for now it seems best not to have parallel workers      * trying to create their own parallel workers.      *      * We can't use parallelism in serializable mode because the predicate      * locking code is not parallel-aware.  It's not catastrophic if someone      * tries to run a parallel plan in serializable mode; it just won't get      * any workers and will run serially.  But it seems like a good heuristic      * to assume that the same serialization level will be in effect at plan      * time and execution time, so don't generate a parallel plan if we're in      * serializable mode.      */     //判断是否可以使用并行模式     //并行是很大的一个话题,有待以后再谈      if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&         IsUnderPostmaster &&         parse->commandType == CMD_SELECT &&         !parse->hasModifyingCTE &&         max_parallel_workers_per_gather > 0 &&         !IsParallelWorker() &&         !IsolationIsSerializable())     {         /* all the cheap tests pass, so scan the query tree */         glob->maxParallelHazard = max_parallel_hazard(parse);         glob->parallelModeOK = (glob->maxParallelHazard != PROPARALLEL_UNSAFE);     }     else     {         /* skip the query tree scan, just assume it's unsafe */         glob->maxParallelHazard = PROPARALLEL_UNSAFE;         glob->parallelModeOK = false;     }      /*      * glob->parallelModeNeeded is normally set to false here and changed to      * true during plan creation if a Gather or Gather Merge plan is actually      * created (cf. create_gather_plan, create_gather_merge_plan).      *      * However, if force_parallel_mode = on or force_parallel_mode = regress,      * then we impose parallel mode whenever it's safe to do so, even if the      * final plan doesn't use parallelism.  It's not safe to do so if the      * query contains anything parallel-unsafe; parallelModeOK will be false      * in that case.  Note that parallelModeOK can't change after this point.      * Otherwise, everything in the query is either parallel-safe or      * parallel-restricted, and in either case it should be OK to impose      * parallel-mode restrictions.  If that ends up breaking something, then      * either some function the user included in the query is incorrectly      * labelled as parallel-safe or parallel-restricted when in reality it's      * parallel-unsafe, or else the query planner itself has a bug.      */     glob->parallelModeNeeded = glob->parallelModeOK &&         (force_parallel_mode != FORCE_PARALLEL_OFF);      /* Determine what fraction of the plan is likely to be scanned */     if (cursorOptions & CURSOR_OPT_FAST_PLAN)//fast-start plan?     {         /*          * We have no real idea how many tuples the user will ultimately FETCH          * from a cursor, but it is often the case that he doesn't want 'em          * all, or would prefer a fast-start plan anyway so that he can          * process some of the tuples sooner.  Use a GUC parameter to decide          * what fraction to optimize for.          */         tuple_fraction = cursor_tuple_fraction;          /*          * We document cursor_tuple_fraction as simply being a fraction, which          * means the edge cases 0 and 1 have to be treated specially here.  We          * convert 1 to 0 ("all the tuples") and 0 to a very small fraction.          */         if (tuple_fraction >= 1.0)             tuple_fraction = 0.0;         else if (tuple_fraction <= 0.0)             tuple_fraction = 1e-10;     }     else     {         /* Default assumption is we need all the tuples */         tuple_fraction = 0.0;     }     //以上:set up for recursive handling of subqueries,为子查询配置处理器(递归方式)     /* primary planning entry point (may recurse for subqueries) */     root = subquery_planner(glob, parse, NULL,                             false, tuple_fraction);//进入子查询优化器      /* Select best Path and turn it into a Plan */     final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);//获得最上层的优化后的Relation     best_path = get_cheapest_fractional_path(final_rel, tuple_fraction);//获得最佳路径      top_plan = create_plan(root, best_path);//创建计划      /*      * If creating a plan for a scrollable cursor, make sure it can run      * backwards on demand.  Add a Material node at the top at need.      */     if (cursorOptions & CURSOR_OPT_SCROLL)     {         if (!ExecSupportsBackwardScan(top_plan))             top_plan = materialize_finished_plan(top_plan);//如果是Scroll游标,在前台执行,那么物化之(数据不能长期占用内存,但又要满足Scroll的要求,需要物化至磁盘中)     }      /*      * Optionally add a Gather node for testing purposes, provided this is      * actually a safe thing to do.      */     if (force_parallel_mode != FORCE_PARALLEL_OFF && top_plan->parallel_safe)//并行执行,添加Gather节点     {         Gather     *gather = makeNode(Gather);          /*          * If there are any initPlans attached to the formerly-top plan node,          * move them up to the Gather node; same as we do for Material node in          * materialize_finished_plan.          */         gather->plan.initPlan = top_plan->initPlan;         top_plan->initPlan = NIL;          gather->plan.targetlist = top_plan->targetlist;         gather->plan.qual = NIL;         gather->plan.lefttree = top_plan;         gather->plan.righttree = NULL;         gather->num_workers = 1;         gather->single_copy = true;         gather->invisible = (force_parallel_mode == FORCE_PARALLEL_REGRESS);          /*          * Since this Gather has no parallel-aware descendants to signal to,          * we don't need a rescan Param.          */         gather->rescan_param = -1;          /*          * Ideally we'd use cost_gather here, but setting up dummy path data          * to satisfy it doesn't seem much cleaner than knowing what it does.          */         gather->plan.startup_cost = top_plan->startup_cost +             parallel_setup_cost;         gather->plan.total_cost = top_plan->total_cost +             parallel_setup_cost + parallel_tuple_cost * top_plan->plan_rows;         gather->plan.plan_rows = top_plan->plan_rows;         gather->plan.plan_width = top_plan->plan_width;         gather->plan.parallel_aware = false;         gather->plan.parallel_safe = false;          /* use parallel mode for parallel plans. */         root->glob->parallelModeNeeded = true;          top_plan = &gather->plan;     }      /*      * If any Params were generated, run through the plan tree and compute      * each plan node's extParam/allParam sets.  Ideally we'd merge this into      * set_plan_references' tree traversal, but for now it has to be separate      * because we need to visit subplans before not after main plan.      */     if (glob->paramExecTypes != NIL)     {         Assert(list_length(glob->subplans) == list_length(glob->subroots));         forboth(lp, glob->subplans, lr, glob->subroots)         {             Plan       *subplan = (Plan *) lfirst(lp);             PlannerInfo *subroot = lfirst_node(PlannerInfo, lr);              SS_finalize_plan(subroot, subplan);         }         SS_finalize_plan(root, top_plan);     }      /* final cleanup of the plan */     Assert(glob->finalrtable == NIL);     Assert(glob->finalrowmarks == NIL);     Assert(glob->resultRelations == NIL);     Assert(glob->nonleafResultRelations == NIL);     Assert(glob->rootResultRelations == NIL);     top_plan = set_plan_references(root, top_plan);     /* ... and the subplans (both regular subplans and initplans) */     Assert(list_length(glob->subplans) == list_length(glob->subroots));     forboth(lp, glob->subplans, lr, glob->subroots)     {         Plan       *subplan = (Plan *) lfirst(lp);         PlannerInfo *subroot = lfirst_node(PlannerInfo, lr);          lfirst(lp) = set_plan_references(subroot, subplan);     }      /* build the PlannedStmt result */     result = makeNode(PlannedStmt);      result->commandType = parse->commandType;     result->queryId = parse->queryId;     result->hasReturning = (parse->returningList != NIL);     result->hasModifyingCTE = parse->hasModifyingCTE;     result->canSetTag = parse->canSetTag;     result->transientPlan = glob->transientPlan;     result->dependsOnRole = glob->dependsOnRole;     result->parallelModeNeeded = glob->parallelModeNeeded;     result->planTree = top_plan;     result->rtable = glob->finalrtable;     result->resultRelations = glob->resultRelations;     result->nonleafResultRelations = glob->nonleafResultRelations;     result->rootResultRelations = glob->rootResultRelations;     result->subplans = glob->subplans;     result->rewindPlanIDs = glob->rewindPlanIDs;     result->rowMarks = glob->finalrowmarks;     result->relationOids = glob->relationOids;     result->invalItems = glob->invalItems;     result->paramExecTypes = glob->paramExecTypes;     /* utilityStmt should be null, but we might as well copy it */     result->utilityStmt = parse->utilityStmt;     result->stmt_location = parse->stmt_location;     result->stmt_len = parse->stmt_len;      result->jitFlags = PGJIT_NONE;     if (jit_enabled && jit_above_cost >= 0 &&         top_plan->total_cost > jit_above_cost)     {         result->jitFlags |= PGJIT_PERFORM;          /*          * Decide how much effort should be put into generating better code.          */         if (jit_optimize_above_cost >= 0 &&             top_plan->total_cost > jit_optimize_above_cost)             result->jitFlags |= PGJIT_OPT3;         if (jit_inline_above_cost >= 0 &&             top_plan->total_cost > jit_inline_above_cost)             result->jitFlags |= PGJIT_INLINE;          /*          * Decide which operations should be JITed.          */         if (jit_expressions)             result->jitFlags |= PGJIT_EXPR;         if (jit_tuple_deforming)             result->jitFlags |= PGJIT_DEFORM;     }      return result; }

subquery_planner

 /*--------------------  * subquery_planner  *    Invokes the planner on a subquery.  We recurse to here for each  *    sub-SELECT found in the query tree.  *  * glob is the global state for the current planner run.  * parse is the querytree produced by the parser & rewriter.  * parent_root is the immediate parent Query's info (NULL at the top level).  * hasRecursion is true if this is a recursive WITH query.  * tuple_fraction is the fraction of tuples we expect will be retrieved.  * tuple_fraction is interpreted as explained for grouping_planner, below.  *  * Basically, this routine does the stuff that should only be done once  * per Query object.  It then calls grouping_planner.  At one time,  * grouping_planner could be invoked recursively on the same Query object;  * that's not currently true, but we keep the separation between the two  * routines anyway, in case we need it again someday.  *  * subquery_planner will be called recursively to handle sub-Query nodes  * found within the query's expressions and rangetable.  *  * Returns the PlannerInfo struct ("root") that contains all data generated  * while planning the subquery.  In particular, the Path(s) attached to  * the (UPPERREL_FINAL, NULL) upperrel represent our conclusions about the  * cheapest way(s) to implement the query.  The top level will select the  * best Path and pass it through createplan.c to produce a finished Plan.  *--------------------  *//*输入:    glob-PlannerGlobal    parse-Query结构体指针    parent_root-父PlannerInfo Root节点    hasRecursion-是否递归?    tuple_fraction-扫描Tuple比例输出:    PlannerInfo指针*/ PlannerInfo * subquery_planner(PlannerGlobal *glob, Query *parse,                  PlannerInfo *parent_root,                  bool hasRecursion, double tuple_fraction) {     PlannerInfo *root;//返回值     List       *newWithCheckOptions;//     List       *newHaving;//Having子句     bool        hasOuterJoins;//是否存在Outer Join?     RelOptInfo *final_rel;//     ListCell   *l;//临时变量      /* Create a PlannerInfo data structure for this subquery */     root = makeNode(PlannerInfo);//构造返回值     root->parse = parse;     root->glob = glob;     root->query_level = parent_root ? parent_root->query_level + 1 : 1;     root->parent_root = parent_root;     root->plan_params = NIL;     root->outer_params = NULL;     root->planner_cxt = CurrentMemoryContext;     root->init_plans = NIL;     root->cte_plan_ids = NIL;     root->multiexpr_params = NIL;     root->eq_classes = NIL;     root->append_rel_list = NIL;     root->rowMarks = NIL;     memset(root->upper_rels, 0, sizeof(root->upper_rels));     memset(root->upper_targets, 0, sizeof(root->upper_targets));     root->processed_tlist = NIL;     root->grouping_map = NULL;     root->minmax_aggs = NIL;     root->qual_security_level = 0;     root->inhTargetKind = INHKIND_NONE;     root->hasRecursion = hasRecursion;     if (hasRecursion)         root->wt_param_id = SS_assign_special_param(root);     else         root->wt_param_id = -1;     root->non_recursive_path = NULL;     root->partColsUpdated = false;      /*      * If there is a WITH list, process each WITH query and build an initplan      * SubPlan structure for it.      */     if (parse->cteList)         SS_process_ctes(root);//处理With 语句      /*      * Look for ANY and EXISTS SubLinks in WHERE and JOIN/ON clauses, and try      * to transform them into joins.  Note that this step does not descend      * into subqueries; if we pull up any subqueries below, their SubLinks are      * processed just before pulling them up.      */     if (parse->hasSubLinks)         pull_up_sublinks(root); //上拉子链接      //其他内容...     return root; }

pull_up_sublinks

下一小节介绍

三、基础信息

数据结构/宏定义
1、cursorOptions

 /* ----------------------  *      Declare Cursor Statement  *  * The "query" field is initially a raw parse tree, and is converted to a  * Query node during parse analysis.  Note that rewriting and planning  * of the query are always postponed until execution.  * ----------------------  */ #define CURSOR_OPT_BINARY       0x0001  /* BINARY */ #define CURSOR_OPT_SCROLL       0x0002  /* SCROLL explicitly given */ #define CURSOR_OPT_NO_SCROLL    0x0004  /* NO SCROLL explicitly given */ #define CURSOR_OPT_INSENSITIVE  0x0008  /* INSENSITIVE */ #define CURSOR_OPT_HOLD         0x0010  /* WITH HOLD */ /* these planner-control flags do not correspond to any SQL grammar: */ #define CURSOR_OPT_FAST_PLAN    0x0020  /* prefer fast-start plan */ #define CURSOR_OPT_GENERIC_PLAN 0x0040  /* force use of generic plan */ #define CURSOR_OPT_CUSTOM_PLAN  0x0080  /* force use of custom  #define CURSOR_OPT_PARALLEL_OK  0x0100  /* parallel mode OK */

2、Relids

 /*  * Relids  *      Set of relation identifiers (indexes into the rangetable).  */ typedef Bitmapset *Relids; /* The unit size can be adjusted by changing these three declarations: */ #define BITS_PER_BITMAPWORD 32 typedef uint32 bitmapword;      /* must be an unsigned type */ typedef int32 signedbitmapword; /* must be the matching signed type */  typedef struct Bitmapset {     int         nwords;         /* number of words in array */     bitmapword  words[FLEXIBLE_ARRAY_MEMBER];   /* really [nwords] */ } Bitmapset;

四、跟踪分析

测试脚本:

testdb=# explain select * from t_dwxx awhere dwbh > all (select b.dwbh from t_grxx b);                                QUERY PLAN                                -------------------------------------------------------------------------- Seq Scan on t_dwxx a  (cost=0.00..1498.00 rows=80 width=474)   Filter: (SubPlan 1)   SubPlan 1     ->  Materialize  (cost=0.00..17.35 rows=490 width=38)           ->  Seq Scan on t_grxx b  (cost=0.00..14.90 rows=490 width=38)(5 rows)

启动gdb跟踪:

(gdb) b pull_up_sublinksBreakpoint 1 at 0x77cbc6: file prepjointree.c, line 157.(gdb) cContinuing.Breakpoint 1, pull_up_sublinks (root=0x126fd48) at prepjointree.c:157157                          (Node *) root->parse->jointree,(gdb) #查看输入参数(gdb) p *root$1 = {type = T_PlannerInfo, parse = 0x11b43d8, glob = 0x11b4e00, query_level = 1, parent_root = 0x0, plan_params = 0x0,   outer_params = 0x0, simple_rel_array = 0x0, simple_rel_array_size = 0, simple_rte_array = 0x0, all_baserels = 0x0,   nullable_baserels = 0x0, join_rel_list = 0x0, join_rel_hash = 0x0, join_rel_level = 0x0, join_cur_level = 0,   init_plans = 0x0, cte_plan_ids = 0x0, multiexpr_params = 0x0, eq_classes = 0x0, canon_pathkeys = 0x0,   left_join_clauses = 0x0, right_join_clauses = 0x0, full_join_clauses = 0x0, join_info_list = 0x0, append_rel_list = 0x0,   rowMarks = 0x0, placeholder_list = 0x0, fkey_list = 0x0, query_pathkeys = 0x0, group_pathkeys = 0x0,   window_pathkeys = 0x0, distinct_pathkeys = 0x0, sort_pathkeys = 0x0, part_schemes = 0x0, initial_rels = 0x0,   upper_rels = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, upper_targets = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},   processed_tlist = 0x0, grouping_map = 0x0, minmax_aggs = 0x0, planner_cxt = 0x11b2e90, total_table_pages = 0,   tuple_fraction = 0, limit_tuples = 0, qual_security_level = 0, inhTargetKind = INHKIND_NONE, hasJoinRTEs = false,   hasLateralRTEs = false, hasDeletedRTEs = false, hasHavingQual = false, hasPseudoConstantQuals = false,   hasRecursion = false, wt_param_id = -1, non_recursive_path = 0x0, curOuterRels = 0x0, curOuterParams = 0x0,   join_search_private = 0x0, partColsUpdated = false}#jointree的类型为FromExpr  (gdb) p *root->parse->jointree$3 = {type = T_FromExpr, fromlist = 0x11b4870, quals = 0x11b40e8}(gdb) n156   jtnode = pull_up_sublinks_jointree_recurse(root,#进入pull_up_sublinks_jointree_recurse函数(gdb) steppull_up_sublinks_jointree_recurse (root=0x126fd48, jtnode=0x1282ea0, relids=0x7ffe830ab9b0) at prepjointree.c:180180   if (jtnode == NULL)#查看参数#1.root与pull_up_sublinks函数的输入一致#2.jtnode类型为FromExpr#3.relids(gdb) p *jtnode$7 = {type = T_FromExpr}(gdb) p *(FromExpr *)jtnode$8 = {type = T_FromExpr, fromlist = 0x11b4870, quals = 0x11b40e8}#FromExpr->fromlist中的的元素类型为RangeTblRef(gdb) p *((FromExpr *)jtnode)->fromlist$9 = {type = T_List, length = 1, head = 0x11b4850, tail = 0x11b4850}(gdb) p *(Node *)((FromExpr *)jtnode)->fromlist->head->data.ptr_value$10 = {type = T_RangeTblRef}...#进入FromExpr分支191   else if (IsA(jtnode, FromExpr))...201     foreach(l, f->fromlist)(gdb) 207                              lfirst(l),(gdb) p *l$11 = {data = {ptr_value = 0x11b4838, int_value = 18565176, oid_value = 18565176}, next = 0x0}(gdb) p *(RangeTblRef *)l->data.ptr_value$13 = {type = T_RangeTblRef, rtindex = 1}#递归调用pull_up_sublinks_jointree_recurse(gdb) n206       newchild = pull_up_sublinks_jointree_recurse(root,(gdb) steppull_up_sublinks_jointree_recurse (root=0x126fd48, jtnode=0x11b4838, relids=0x7ffe830ab940) at prepjointree.c:180180   if (jtnode == NULL)#输入参数#1.root与pull_up_sublinks函数的输入一致#2.jtnode类型为RangeTblRef#3.relids#进入RangeTblRef分支(gdb) n184   else if (IsA(jtnode, RangeTblRef))(gdb) n186     int     varno = ((RangeTblRef *) jtnode)->rtindex;(gdb) 188     *relids = bms_make_singleton(varno);(gdb) 312   return jtnode;(gdb) p *relids$16 = (Relids) 0x12704c8...(gdb) p *newchild$19 = {type = T_RangeTblRef}(gdb) n210       frelids = bms_join(frelids, childrelids);(gdb) p *frelids$25 = {nwords = 1, words = 0x12704cc}(gdb) p *frelids->words$26 = 2...#进入pull_up_sublinks_qual_recurse(gdb) n217     newf->quals = pull_up_sublinks_qual_recurse(root, f->quals,(gdb) steppull_up_sublinks_qual_recurse (root=0x126fd48, node=0x11b40e8, jtlink1=0x7ffe830ab948, available_rels1=0x12704c8,     jtlink2=0x0, available_rels2=0x0) at prepjointree.c:335335   if (node == NULL)#输入参数#1.root与上述无异#2.node(gdb) p *node$35 = {type = T_SubLink}(gdb) p *(SubLink *)node$36 = {xpr = {type = T_SubLink}, subLinkType = ALL_SUBLINK, subLinkId = 0, testexpr = 0x1282e00, operName = 0x11b3cf8,   subselect = 0x1282578, location = 35}#3.jtlink1,指针数组(gdb) p *(RangeTblRef *)((FromExpr *)jtlink1[0])->fromlist->head->data->ptr_value$45 = {type = T_RangeTblRef, rtindex = 1}#4.available_rels1,可用的Relids(gdb) p *available_rels1$47 = {nwords = 1, words = 0x12704cc}(gdb) p *available_rels1->words$48 = 2#5/6,NULL值(gdb) n337   if (IsA(node, SubLink))(gdb) 339     SubLink    *sublink = (SubLink *) node;(gdb) 344     if (sublink->subLinkType == ANY_SUBLINK)(gdb) 398     else if (sublink->subLinkType == EXISTS_SUBLINK)(gdb) p sublink->subLinkType$49 = ALL_SUBLINK#非ANY/EXISTS链接,退出(gdb) n453     return node;(gdb) #回到pull_up_sublinks_jointree_recurse(gdb) pull_up_sublinks_jointree_recurse (root=0x126fd48, jtnode=0x1282ea0, relids=0x7ffe830ab9b0) at prepjointree.c:230230     *relids = frelids;(gdb) 231     jtnode = jtlink;(gdb) 312   return jtnode;#返回的jtnode为RangeTblRef(gdb) p *(RangeTblRef *)((FromExpr *)jtnode)->fromlist->head->data.ptr_value$55 = {type = T_RangeTblRef, rtindex = 1}(gdb) n313 }(gdb) pull_up_sublinks (root=0x126fd48) at prepjointree.c:164164   if (IsA(jtnode, FromExpr))(gdb) 165     root->parse->jointree = (FromExpr *) jtnode;(gdb) p *root->parse->jointree$56 = {type = T_FromExpr, fromlist = 0x11b4870, quals = 0x11b40e8}(gdb) p *root->parse->jointree->fromlist$57 = {type = T_List, length = 1, head = 0x11b4850, tail = 0x11b4850}(gdb) n168 }(gdb) subquery_planner (glob=0x11b4e00, parse=0x11b43d8, parent_root=0x0, hasRecursion=false, tuple_fraction=0) at planner.c:656656   inline_set_returning_functions(root);(gdb) cContinuing.#ANY类型并没有做上拉操作

感谢你能够认真阅读完这篇文章,希望小编分享的"PostgreSQL如何实现上拉子链接"这篇文章对大家有帮助,同时也希望大家多多支持,关注行业资讯频道,更多相关知识等着你来学习!

0