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 按照的表示中 节点 的类型, 分为两种执行方式
- EEOP_PARAM_EXEC , 节点为 Param 的时候, 对应执行时函数为 ExecEvalParamExec
- 初始化 subplan 的时候,判断存在 setParam 且 不存在 parParam 的时候,设置 prm 的 execPlan 为当前的 subplanstats, 表示执行为initplan, 且需要执行子查询
- 执行的时候,判断 execPlan 存在,则使用 ExecSetParamPlan 执行 子查询, 且 按照 setParam 获取对应的 prm
- 设置 prm 的值, 表示的是 initplan 的返回值
- 设置 execPlan 为 null, 表示initplan 执行完毕,后续不再执行,后续直接使用 prm 的值即可
- 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 中的数据
- 不存在 testexpr, subplan 的返回值需要返回到 表达式的计算过程中参与计算。类似
1 expr subplan 单层为什么rescan没问题