typedef struct SubPlan
{
  pg_node_attr(no_query_jumble)

  Expr    xpr;
  /* Fields copied from original SubLink: */
  SubLinkType subLinkType;  /* see above */
  /* The combining operators, transformed to an executable expression: */
  Node     *testexpr;       /* OpExpr or RowCompareExpr expression tree */
  List     *paramIds;       /* IDs of Params embedded in the above */
  /* Identification of the Plan tree to use: */
  int      plan_id;         /* Index (from 1) in PlannedStmt.subplans */
  /* Identification of the SubPlan for EXPLAIN and debugging purposes: */
  char     *plan_name;      /* A name assigned during planning */
  /* Extra data useful for determining subplan's output type: */
  Oid      firstColType;    /* Type of first column of subplan result */
  int32    firstColTypmod;  /* Typmod of first column of subplan result */
  Oid      firstColCollation;  /* Collation of first column of subplan result */
  /* Information about execution strategy: */
  bool    useHashTable;   /* true to store subselect output in a hash table (implies we are doing "IN") */
  bool    unknownEqFalse; /* true if it's okay to return FALSE when the
                 * spec result is UNKNOWN; this allows much
                 * simpler handling of null values */
  bool    parallel_safe;  /* is the subplan parallel-safe? */
  /* Note: parallel_safe does not consider contents of testexpr or args */
  /* Information for passing params into and out of the subselect: */
  /* setParam and parParam are lists of integers (param IDs) */
  //  如果存在 setParam ,在初始化 SubPlanState 的时候,会把 SubPlanState 添加到 paramid 对应的 prm 中去,在执行的时候,
  //  遇到 Param 的时候会执行 ExecEvalParamExec 函数,此时由于存在 SubPlanState ,会去执行 ExecSetParamPlan , 去执行initplan
  //  但是 初始化的时候要求 parParam 为 nil, 此时不会设置的 SubPlanState , ExecEvalParamExec 也不会执行 ExecSetParamPlan ,而是直接获取 prm 
  //  中 提前设置的值
  //  在 initplan 执行过之后,会设置 execPlan 为null, 之后直接使用 prm 中得 值即可, 这也代表 init plan 只会返回一行
  List     *setParam;     /* initplan and MULTIEXPR subqueries have to set these Params for parent plan */
  //  parParam 和 args 是一一对应的,args 是 Var list,在 ExecScanSubPlan 中, 会使用 args 去 slot 中获取对应的值,
  //  然后 使用 parParam 获取对应下标 的 prm, 然后设置值, 此时 parent 参数准备完毕,在后续执行中遇到 Param , 执行 ExecEvalParamExec 的时候,则直接使用 prm 的值即可, 
  //  如果 存在 testexpr , 则需要 再执行 testexpr , 否则直接输出 子查询的结果,然后再 执行对应的比较表达式
  List     *parParam;     /* indices of input Params from parent plan */
  List     *args;         /* exprs to pass as parParam values */
  /* Estimated execution costs: */
  Cost    startup_cost;   /* one-time setup cost */
  Cost    per_call_cost;  /* cost for each subplan evaluation */
} SubPlan;
  • SS_replace_correlation_vars 遍历子查询中的,上层引用,替换为 param ,并且 对应 层级 得 root 的 plan_params 会记录替换的 表达式,

$150 = [opno: 96, opfuncid: 65, opresulttype: 16, opretset false, opcollid 0, inputcollid 0] = { args = List with 2 elements = { 0 = [varno: 1, varattno: 1, vartype: 23, vartypmod: -1, varcollid: 0, varlevelsup: 0, varnosyn: 1, varattnosyn: 1], 1 = [varno: 1, varattno: 1, vartype: 23, vartypmod: -1, varcollid: 0, varlevelsup: 1, varnosyn: 1, varattnosyn: 1] } }

$153 = [opno: 96, opfuncid: 65, opresulttype: 16, opretset false, opcollid 0, inputcollid 0] = { args = List with 2 elements = { 0 = [varno: 1, varattno: 1, vartype: 23, vartypmod: -1, varcollid: 0, varlevelsup: 0, varnosyn: 1, varattnosyn: 1], 1 = Param[paramkind: PARAM_EXEC, paramid: 0, paramtype: 23, paramtypmod: -1, paramcollid: 0] } }

  • build_subplan

    • plan_params 不为null,则使用 plan_params 构造 parParam 和 args

    • parParam 是 paramId , args 是 对应得 var

    • 代表得是上层引用

    • 如果为空,表示没有上层引用,表示 为 initplan

    • 此时构建Param 作为result 返回

    • 同时 setParam 表示下层对应输出的targetlist 中的var

    • 存在 testexpr 的时候, 则会把对应的 param 的 id 添加到 paramIds 中

    • 添加subplan 到 global subplans 中, initplan 的时候会初始化到 es_subplanstates 中

  • execute 按照的表示中 节点 的类型, 分为两种执行方式

    1. EEOP_PARAM_EXEC , 节点为 Param 的时候, 对应执行时函数为 ExecEvalParamExec
    • 初始化 subplan 的时候,判断存在 setParam 且 不存在 parParam 的时候,设置 prm 的 execPlan 为当前的 subplanstats, 表示执行为initplan, 且需要执行子查询
    • 执行的时候,判断 execPlan 存在,则使用 ExecSetParamPlan 执行 子查询, 且 按照 setParam 获取对应的 prm
      • 设置 prm 的值, 表示的是 initplan 的返回值
      • 设置 execPlan 为 null, 表示initplan 执行完毕,后续不再执行,后续直接使用 prm 的值即可
    1. EEOP_SUBPLAN , 节点为 subplan 的时候,对应的执行函数为 ExecEvalSubPlan
    • subplan 有两种特殊情况
      • 不存在 testexpr, subplan 的返回值需要返回到 表达式的计算过程中参与计算。类似 Filter: (a <= (SubPlan 1))
      • 存在 testexpr, subplan 中,直接计算表达式,然后返回一个bool值即可,类似 Filter: (SubPlan 1)
      • 执行时,作为 qual 存在,此时 econtext->ecxt_scantuple 已存在,在 函数 ExecScanSubPlan 中,
        • 首先遍历 parParam 和 args, 使用args 从 ecxt_scantuple 中获取数据, 然后使用 parParam 获取 prm 把数据设置到 prm 中
        • 然后执行子查询, 子查询中遇到 需要外层数据的时候,直接从这里设置的 prm 中获取
        • 如果不存在 testexpr, 可以直接返回
        • 如果存在 testexpr, 则会遍历 paramIds ,从slot 中获取数据, 设置到 prm 中,然后 计算表达式, 表达式中 会使用 prm 中的数据
  • 1 expr subplan 单层为什么rescan没问题