diff --git a/src/gausskernel/runtime/executor/execUtils.cpp b/src/gausskernel/runtime/executor/execUtils.cpp index 84fd23666..e6e59d64e 100644 --- a/src/gausskernel/runtime/executor/execUtils.cpp +++ b/src/gausskernel/runtime/executor/execUtils.cpp @@ -1,44 +1,44 @@ /* ------------------------------------------------------------------------- * * execUtils.cpp - * miscellaneous executor utility routines + * 杂项执行器实用程序函数 * - * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * Portions Copyright (c) 2021, openGauss Contributors + * 版权所有 (c) 2020 华为技术有限公司 + * 版权所有 (c) 1996-2012,PostgreSQL全球开发团队 + * 版权所有 (c) 1994,加州大学监管机构 + * 版权所有 (c) 2021,openGauss 社区贡献者 * * - * IDENTIFICATION + * 识别码 * src/gausskernel/runtime/executor/execUtils.cpp * * ------------------------------------------------------------------------- - * INTERFACE ROUTINES - * CreateExecutorState Create/delete executor working state + + * 接口函数 + * CreateExecutorState 创建/删除执行器工作状态 * FreeExecutorState * CreateExprContext * CreateStandaloneExprContext * FreeExprContext * ReScanExprContext * - * ExecAssignExprContext Common code for plan node init routines. + * ExecAssignExprContext 计划节点初始化例程的通用代码。 * ExecAssignResultType - * etc + * 等等 * - * ExecOpenScanRelation Common code for scan node init routines. + * ExecOpenScanRelation 扫描节点初始化例程的通用代码。 * ExecCloseScanRelation * * ExecOpenIndices \ - * ExecCloseIndices | referenced by InitPlan, EndPlan, - * ExecInsertIndexTuples / ExecInsert, ExecUpdate + * ExecCloseIndices | 被 InitPlan、EndPlan、ExecInsert、ExecUpdate 引用 + * ExecInsertIndexTuples / * - * RegisterExprContextCallback Register function shutdown callback - * UnregisterExprContextCallback Deregister function shutdown callback + * RegisterExprContextCallback 注册函数关机回调 + * UnregisterExprContextCallback 注销函数关机回调 * - * NOTES - * This file has traditionally been the place to stick misc. - * executor support stuff that doesn't really go anyplace else. - */ + * 注意 + * 这个文件传统上是放置一些其他地方不太适合的杂项执行器支持代码的地方。 + #include "postgres.h" #include "knl/knl_variable.h" @@ -74,30 +74,31 @@ static bool check_violation(Relation heap, Relation index, IndexInfo *indexInfo, Oid *conflictPartOid = NULL, int2 *conflictBucketid = NULL); /* ---------------------------------------------------------------- - * Executor state and memory management functions + * 执行器状态和内存管理函数 * ---------------------------------------------------------------- - */ + + + /* ---------------- * CreateExecutorState * - * Create and initialize an EState node, which is the root of - * working storage for an entire Executor invocation. + * 创建并初始化一个 EState 节点,它是整个执行器调用的工作存储的根节点。 * - * Principally, this creates the per-query memory context that will be - * used to hold all working data that lives till the end of the query. - * Note that the per-query context will become a child of the caller's - * CurrentMemoryContext. + * 主要来说,这个函数创建了一个用于存储整个查询期间持续存在的所有工作数据的每个查询内存上下文。 + * 需要注意的是,每个查询上下文将成为调用者的 CurrentMemoryContext 的子上下文。 * ---------------- */ + EState* CreateExecutorState(MemoryContext saveCxt) { EState* estate = NULL; MemoryContext qcontext; MemoryContext oldcontext; - /* - * Create the per-query context for this Executor run. + /* + * 为此 Executor 运行创建每个查询的上下文。 */ + if (saveCxt != NULL) { qcontext = saveCxt; } else { @@ -108,16 +109,16 @@ EState* CreateExecutorState(MemoryContext saveCxt) ALLOCSET_DEFAULT_MAXSIZE); } - /* - * Make the EState node within the per-query context. This way, we don't - * need a separate pfree_ext() operation for it at shutdown. + /* + * 在每个查询的上下文中创建 EState 节点。这样,我们在关闭时不需要单独的 pfree_ext() 操作。 */ + oldcontext = MemoryContextSwitchTo(qcontext); estate = makeNode(EState); - /* - * Initialize all fields of the Executor State structure + /* + * 初始化 Executor 状态结构的所有字段 */ estate->es_direction = ForwardScanDirection; estate->es_snapshot = SnapshotNow; @@ -146,7 +147,8 @@ EState* CreateExecutorState(MemoryContext saveCxt) estate->es_param_exec_vals = NULL; estate->es_query_cxt = qcontext; - estate->es_const_query_cxt = qcontext; /* context query context, it will not be changed */ + estate->es_const_query_cxt = qcontext;/* 查询上下文的上下文,它不会被更改 */ + estate->es_tupleTable = NIL; estate->es_epqTupleSlot = NULL; @@ -184,86 +186,77 @@ EState* CreateExecutorState(MemoryContext saveCxt) estate->pruningResult = NULL; - /* - * Return the executor state structure - */ + /* + * 返回执行器状态结构 + */ + MemoryContextSwitchTo(oldcontext); return estate; } -/* ---------------- - * FreeExecutorState +/* + * 释放EState及其所有剩余的工作存储空间。 * - * Release an EState along with all remaining working storage. + * 注意:这不负责释放非内存资源,如打开的关系或缓冲区引用。但它会关闭EState中的任何仍处于活动状态的ExprContext。 + * 这足够清理仅用于表达式求值而不用于运行完整计划的情况。 * - * Note: this is not responsible for releasing non-memory resources, - * such as open relations or buffer pins. But it will shut down any - * still-active ExprContexts within the EState. That is sufficient - * cleanup for situations where the EState has only been used for expression - * evaluation, and not to run a complete Plan. - * - * This can be called in any memory context ... so long as it's not one - * of the ones to be freed. - * ---------------- + * 这可以在任何内存上下文中调用...只要不是要释放的那些之一。 */ + void FreeExecutorState(EState* estate) { - /* - * Shut down and free any remaining ExprContexts. We do this explicitly - * to ensure that any remaining shutdown callbacks get called (since they - * might need to release resources that aren't simply memory within the - * per-query memory context). - */ + /* + * 关闭和释放任何剩余的ExprContexts。我们明确执行此操作以确保调用任何剩余的关闭回调(因为它们可能需要释放的资源不仅仅是在每个查询内存上下文中的内存)。 + */ + while (estate->es_exprcontexts) { - /* - * XXX: seems there ought to be a faster way to implement this than - * repeated list_delete(), no? - */ + /* + * XXX:似乎应该有一种比反复使用list_delete()更快的方法来实现这个操作,不是吗? + */ + FreeExprContext((ExprContext*)linitial(estate->es_exprcontexts), true); - /* FreeExprContext removed the list link for us */ + /* FreeExprContext 为我们移除了链表的链接 */ + } - /* - * Free the per-query memory context, thereby releasing all working - * memory, including the EState node itself. - */ + /* + * 释放 per-query 内存上下文,从而释放所有的工作内存,包括 EState 结构本身。 + */ + MemoryContextDelete(estate->es_query_cxt); } -/* ---------------- - * CreateExprContext +/* + * 创建一个在 EState 内部用于表达式评估的上下文。 * - * Create a context for expression evaluation within an EState. + * 执行程序运行可能需要多个 ExprContexts(通常我们为每个计划节点创建一个,还有一个单独的用于每个输出元组处理,例如约束检查)。 + * 每个 ExprContext 都有它自己的 "每个元组" 内存上下文。 * - * An executor run may require multiple ExprContexts (we usually make one - * for each Plan node, and a separate one for per-output-tuple processing - * such as constraint checking). Each ExprContext has its own "per-tuple" - * memory context. - * - * Note we make no assumption about the caller's memory context. - * ---------------- + * 注意,我们不对调用者的内存上下文做任何假设。 */ + ExprContext* CreateExprContext(EState* estate) { ExprContext* econtext = NULL; MemoryContext oldcontext; - /* Create the ExprContext node within the per-query memory context */ + /* 在每个查询的内存上下文中创建 ExprContext 节点 */ + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); econtext = makeNode(ExprContext); - /* Initialize fields of ExprContext */ + /* 初始化 ExprContext 的字段 */ + econtext->ecxt_scantuple = NULL; econtext->ecxt_innertuple = NULL; econtext->ecxt_outertuple = NULL; econtext->ecxt_per_query_memory = estate->es_query_cxt; - /* - * Create working memory for expression evaluation in this context. - */ + /* 在该上下文中为表达式评估创建工作内存。 */ + econtext->ecxt_per_tuple_memory = AllocSetContextCreate(estate->es_query_cxt, "ExprContext", ALLOCSET_DEFAULT_MINSIZE, @@ -287,11 +280,7 @@ ExprContext* CreateExprContext(EState* estate) econtext->ecxt_callbacks = NULL; econtext->plpgsql_estate = NULL; - /* - * Link the ExprContext into the EState to ensure it is shut down when the - * EState is freed. Because we use lcons(), shutdowns will occur in - * reverse order of creation, which may not be essential but can't hurt. - */ + /* 将ExprContext链接到EState,以确保在释放EState时关闭它。由于我们使用lcons(),关闭将按照创建的相反顺序发生,这可能不是必需的,但不会有害。 */ estate->es_exprcontexts = lcons(econtext, estate->es_exprcontexts); MemoryContextSwitchTo(oldcontext); @@ -302,38 +291,31 @@ ExprContext* CreateExprContext(EState* estate) /* ---------------- * CreateStandaloneExprContext * - * Create a context for standalone expression evaluation. + * 创建一个用于独立表达式评估的上下文。 * - * An ExprContext made this way can be used for evaluation of expressions - * that contain no Params, subplans, or Var references (it might work to - * put tuple references into the scantuple field, but it seems unwise). + * 通过这种方式创建的ExprContext可用于评估不包含Params、子计划或Var引用的表达式(可能将元组引用放入scantuple字段是可行的,但似乎不明智)。 * - * The ExprContext struct is allocated in the caller's current memory - * context, which also becomes its "per query" context. + * ExprContext结构在调用者的当前内存上下文中分配,该内存上下文也成为其“每个查询”的上下文。 * - * It is caller's responsibility to free the ExprContext when done, - * or at least ensure that any shutdown callbacks have been called - * (ReScanExprContext() is suitable). Otherwise, non-memory resources - * might be leaked. + * 在完成后,调用者有责任释放ExprContext,或者至少确保已调用任何关闭回调函数(ReScanExprContext()是合适的)。否则,可能会泄漏非内存资源。 * ---------------- */ + ExprContext* CreateStandaloneExprContext(void) { ExprContext* econtext = NULL; - /* Create the ExprContext node within the caller's memory context */ + /* 在调用者的内存上下文中创建ExprContext节点 */ econtext = makeNode(ExprContext); - /* Initialize fields of ExprContext */ + /* 初始化ExprContext的字段 */ econtext->ecxt_scantuple = NULL; econtext->ecxt_innertuple = NULL; econtext->ecxt_outertuple = NULL; econtext->ecxt_per_query_memory = CurrentMemoryContext; - /* - * Create working memory for expression evaluation in this context. - */ + /* 在这个上下文中为表达式评估创建工作内存 */ econtext->ecxt_per_tuple_memory = AllocSetContextCreate(CurrentMemoryContext, "ExprContext", ALLOCSET_DEFAULT_MINSIZE, @@ -359,62 +341,55 @@ ExprContext* CreateStandaloneExprContext(void) return econtext; } -/* ---------------- - * FreeExprContext - * - * Free an expression context, including calling any remaining - * shutdown callbacks. - * - * Since we free the temporary context used for expression evaluation, - * any previously computed pass-by-reference expression result will go away! - * - * If isCommit is false, we are being called in error cleanup, and should - * not call callbacks but only release memory. (It might be better to call - * the callbacks and pass the isCommit flag to them, but that would require - * more invasive code changes than currently seems justified.) - * - * Note we make no assumption about the caller's memory context. - * ---------------- - */ +/* 释放表达式上下文,包括调用任何剩余的关闭回调函数。 + +由于我们释放了用于表达式评估的临时上下文,任何先前计算的传递引用表达式结果都会被清除! + +如果 isCommit 为 false,则我们是在错误清理中调用,不应调用回调函数,只能释放内存。 +(可能更好的方法是调用回调函数并将 isCommit 标志传递给它们,但这需要比当前看起来合理的更深入的代码更改。) + +注意,我们不对调用者的内存上下文作任何假设。 +*/ + void FreeExprContext(ExprContext* econtext, bool isCommit) { EState* estate = NULL; - /* Call any registered callbacks */ + /* 调用所有已注册的回调函数 */ ShutdownExprContext(econtext, isCommit); - /* And clean up the memory used */ + /* 然后清理使用的内存 */ MemoryContextDelete(econtext->ecxt_per_tuple_memory); - /* Unlink self from owning EState, if any */ + /* 如果有的话,从拥有它的 EState 中解除链接 */ estate = econtext->ecxt_estate; if (estate != NULL) estate->es_exprcontexts = list_delete_ptr(estate->es_exprcontexts, econtext); - /* And delete the ExprContext node */ + /* 然后删除 ExprContext 节点 */ pfree_ext(econtext); } /* * ReScanExprContext * - * Reset an expression context in preparation for a rescan of its - * plan node. This requires calling any registered shutdown callbacks, - * since any partially complete set-returning-functions must be canceled. + * 在重新扫描计划节点之前,重置表达式上下文。这需要调用任何已注册的关闭回调, + * 因为任何部分完成的返回集函数必须被取消。 * - * Note we make no assumption about the caller's memory context. + * 注意,我们不对调用者的内存上下文做任何假设。 */ + void ReScanExprContext(ExprContext* econtext) { - /* Call any registered callbacks */ + /* 调用任何已注册的回调函数 */ ShutdownExprContext(econtext, true); - /* And clean up the memory used */ + /* 清理使用的内存 */ MemoryContextReset(econtext->ecxt_per_tuple_memory); } /* - * Build a per-output-tuple ExprContext for an EState. + * 为 EState 构建一个每个输出元组的 ExprContext。 * - * This is normally invoked via GetPerTupleExprContext() macro, - * not directly. + * 通常通过 GetPerTupleExprContext() 宏调用,而不是直接调用。 */ + ExprContext* MakePerTupleExprContext(EState* estate) { if (estate->es_per_tuple_exprcontext == NULL) @@ -423,22 +398,19 @@ ExprContext* MakePerTupleExprContext(EState* estate) return estate->es_per_tuple_exprcontext; } -/* ---------------------------------------------------------------- - * miscellaneous node-init support functions +/* + * 杂项节点初始化支持函数 * - * Note: all of these are expected to be called with CurrentMemoryContext - * equal to the per-query memory context. - * ---------------------------------------------------------------- + * 注意:所有这些函数都期望在当前内存上下文为每个查询的内存上下文时调用。 */ -/* ---------------- - * ExecAssignExprContext + +/* + * ExecAssignExprContext * - * This initializes the ps_ExprContext field. It is only necessary - * to do this for nodes which use ExecQual or ExecProject - * because those routines require an econtext. Other nodes that - * don't have to evaluate expressions don't need to do this. - * ---------------- + * 初始化 ps_ExprContext 字段。只有使用 ExecQual 或 ExecProject 的节点需要这样做, + * 因为这些例程需要一个表达式上下文(econtext)。不需要评估表达式的其他节点不需要执行此操作。 */ + void ExecAssignExprContext(EState* estate, PlanState* planstate) { planstate->ps_ExprContext = CreateExprContext(estate); @@ -465,17 +437,15 @@ void ExecAssignResultTypeFromTL(PlanState* planstate, TableAmType tam) TupleDesc tupDesc; if (ExecContextForcesOids(planstate, &hasoid)) { - /* context forces OID choice; hasoid is now set correctly */ + /* context 强制 OID 选择;现在 hasoid 被正确设置 */ } else { - /* given free choice, don't leave space for OIDs in result tuples */ + /* 在给定自由选择的情况下,不要在结果元组中留出 OID 的空间 */ hasoid = false; } - /* - * ExecTypeFromTL needs the parse-time representation of the tlist, not a - * list of ExprStates. This is good because some plan nodes don't bother - * to set up planstate->targetlist ... - */ + /* ExecTypeFromTL 需要 tlist 的解析时表示,而不是 ExprStates 的列表。 + * 这很好,因为某些计划节点不会费心设置 planstate->targetlist ... + */ tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid, false, tam); ExecAssignResultType(planstate, tupDesc); } @@ -487,7 +457,7 @@ void ExecAssignResultTypeFromTL(PlanState* planstate, TableAmType tam) TupleDesc ExecGetResultType(PlanState* planstate) { TupleTableSlot* slot = NULL; - /* if the child node is PartIteratorState, overhead to it's child node */ + /* 如果子节点是 PartIteratorState,则将开销传递给其子节点 */ if (IsA(planstate, PartIteratorState) || IsA(planstate, VecPartIteratorState)) { planstate = outerPlanState(planstate); } @@ -516,8 +486,9 @@ void ExecAssignVectorForExprEval(ExprContext* econtext) econtext->caseValue_vector->init(CurrentMemoryContext, unknownDesc); } -/* Support info for column store.*/ -/* targetList is given from ExprState tree, qual is given from Expr node tree.*/ +/* 用于列存储的支持信息。 */ + +/* targetList 是从 ExprState 树中获取的,qual 是从 Expr 节点树中获取的。 */ static void GetAccessedVarNumbers(ProjectionInfo* projInfo, List* targetList, List* qual) { List* vars = NIL; @@ -534,7 +505,7 @@ static void GetAccessedVarNumbers(ProjectionInfo* projInfo, List* targetList, Li GenericExprState* gstate = (GenericExprState*)lfirst(l); TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr; - /* Pull vars from the targetlist .*/ + /* 从目标列表中提取变量。 */ vars = pull_var_clause((Node*)tle, PVC_RECURSE_AGGREGATES, PVC_RECURSE_PLACEHOLDERS); foreach (vl, vars) { @@ -550,12 +521,12 @@ static void GetAccessedVarNumbers(ProjectionInfo* projInfo, List* targetList, Li } } - /* - * Used for PackT optimization: PackTCopyVarsList records those columns what we need to move. - */ + /* + * 用于 PackT 优化:PackTCopyVarsList 记录需要移动的那些列。 + */ List* PackTCopyVarsList = list_copy(varattno_list); - /* Now consider the quals */ + /* 现在考虑条件表达式(quals) */ vars = pull_var_clause((Node*)qual, PVC_RECURSE_AGGREGATES, PVC_RECURSE_PLACEHOLDERS); foreach (l, vars) { Var* var = (Var*)lfirst(l); @@ -573,10 +544,9 @@ static void GetAccessedVarNumbers(ProjectionInfo* projInfo, List* targetList, Li isConst = true; } - // Now we need get which var can be late accessed. - // In other words, these columns can be load after filter - // We can read these columns as late as possible - // + // 现在我们需要确定哪些变量可以被延迟访问。 +// 换句话说,这些列可以在过滤后加载。 +// 我们可以尽可能晚地读取这些列。 if (qualVarNoList != NIL) { lateAccessVarNoList = list_difference_int(varattno_list, qualVarNoList); list_free_ext(qualVarNoList); @@ -586,8 +556,8 @@ static void GetAccessedVarNumbers(ProjectionInfo* projInfo, List* targetList, Li PackLateAccessList = list_difference_int(PackTCopyVarsList, lateAccessVarNoList); } - /* - * Here projInfo->pi_PackTCopyVars records the specific column data what we want. + /* + * 这里,projInfo->pi_PackTCopyVars 记录了我们想要的特定列数据。 */ projInfo->pi_PackTCopyVars = PackTCopyVarsList; projInfo->pi_acessedVarNumbers = varattno_list; @@ -601,7 +571,7 @@ List* GetAccessedVarnoList(List* targetList, List* qual) { ProjectionInfo tmp_pi; - /* get accessed attno of this query statement */ + /* 获取此查询语句的已访问的属性号(列号) */ GetAccessedVarNumbers(&tmp_pi, targetList, qual); if (tmp_pi.pi_PackTCopyVars) { list_free_ext(tmp_pi.pi_PackTCopyVars); @@ -623,14 +593,14 @@ ProjectionInfo* ExecBuildVecProjectionInfo( bool directMap = false; ListCell* tl = NULL; - // Guard for zero length projection + // 保护零长度投影 // if (len == 0) return NULL; projInfo->pi_exprContext = econtext; projInfo->pi_slot = slot; - /* since these are all int arrays, we need do just one palloc */ + // 由于这些都是整数数组,我们只需要执行一次 palloc 操作 workspace = (int*)palloc(len * 3 * sizeof(int)); projInfo->pi_varSlotOffsets = varSlotOffsets = workspace; projInfo->pi_varNumbers = varNumbers = workspace + len; @@ -638,20 +608,18 @@ ProjectionInfo* ExecBuildVecProjectionInfo( projInfo->pi_lastInnerVar = 0; projInfo->pi_lastOuterVar = 0; projInfo->pi_lastScanVar = 0; - /* Support info for column store.*/ + /* 列存储的支持信息 */ GetAccessedVarNumbers(projInfo, targetList, nt_qual); - // Allocate batch for current project. + // 为当前的投影操作分配批处理内存。 // projInfo->pi_batch = New(CurrentMemoryContext) VectorBatch(CurrentMemoryContext, slot->tts_tupleDescriptor); - /* - * We separate the target list elements into simple Var references and - * expressions which require the full ExecTargetList machinery. To be a - * simple Var, a Var has to be a user attribute and not mismatch the - * inputDesc. (Note: if there is a type mismatch then ExecEvalVar will - * probably throw an error at runtime, but we leave that to it.) - */ + /* + * 我们将目标列表元素分为简单的 Var 引用和需要完整 ExecTargetList 机制的表达式。 + * 要成为一个简单的 Var,Var 必须是用户属性,并且不与输入描述不匹配。 + * (注意:如果存在类型不匹配,那么 ExecEvalVar 在运行时可能会引发错误,但我们将其留给它处理。) + */ exprlist = NIL; numSimpleVars = 0; directMap = true; @@ -662,7 +630,7 @@ ProjectionInfo* ExecBuildVecProjectionInfo( if (variable != NULL && IsA(variable, Var) && variable->varattno > 0) { if (!inputDesc) - isSimpleVar = true; /* can't check type, assume OK */ + isSimpleVar = true; /* 无法检查类型,假设是没问题的 */ else if (variable->varattno <= inputDesc->natts) { Form_pg_attribute attr; @@ -702,9 +670,9 @@ ProjectionInfo* ExecBuildVecProjectionInfo( } numSimpleVars++; } else { - /* Not a simple variable, add it to generic targetlist */ + /* 不是一个简单的变量,将其添加到通用目标列表中 */ exprlist = lappend(exprlist, gstate); - /* Examine expr to include contained Vars in lastXXXVar counts */ + /* 检查表达式以包括在 lastXXXVar 计数中包含的变量 */ get_last_attnums((Node*)variable, projInfo); } } @@ -735,20 +703,12 @@ ProjectionInfo* ExecBuildVecProjectionInfo( return projInfo; } -/* ---------------- - * ExecBuildProjectionInfo - * - * Build a ProjectionInfo node for evaluating the given tlist in the given - * econtext, and storing the result into the tuple slot. (Caller must have - * ensured that tuple slot has a descriptor matching the tlist!) Note that - * the given tlist should be a list of ExprState nodes, not Expr nodes. - * - * inputDesc can be NULL, but if it is not, we check to see whether simple - * Vars in the tlist match the descriptor. It is important to provide - * inputDesc for relation-scan plan nodes, as a cross check that the relation - * hasn't been changed since the plan was made. At higher levels of a plan, - * there is no need to recheck. - * ---------------- +/* 构建 ProjectionInfo 结构,用于在给定的 econtext 中计算给定的 tlist,并将结果存储到元组槽中。 + * (调用者必须确保元组槽具有与 tlist 匹配的描述符!)注意,给定的 tlist 应该是 ExprState 节点的列表,而不是 Expr 节点。 + + * inputDesc 可以为 NULL,但如果不为 NULL,则我们会检查 tlist 中的简单变量是否与描述符匹配。 + * 为了关系扫描计划节点,提供 inputDesc 是很重要的,因为它是检查关系在计划生成后是否发生了更改的交叉检查。 + * 在计划的更高级别,无需重新检查。 */ ProjectionInfo* ExecBuildProjectionInfo( List* targetList, ExprContext* econtext, TupleTableSlot* slot, TupleDesc inputDesc) @@ -766,7 +726,7 @@ ProjectionInfo* ExecBuildProjectionInfo( projInfo->pi_exprContext = econtext; projInfo->pi_slot = slot; - /* since these are all int arrays, we need do just one palloc */ + /* 由于这些都是 int 数组,我们只需要进行一次内存分配(palloc) */ workspace = (int*)palloc(len * 3 * sizeof(int)); projInfo->pi_varSlotOffsets = varSlotOffsets = workspace; projInfo->pi_varNumbers = varNumbers = workspace + len; @@ -775,13 +735,11 @@ ProjectionInfo* ExecBuildProjectionInfo( projInfo->pi_lastOuterVar = 0; projInfo->pi_lastScanVar = 0; - /* - * We separate the target list elements into simple Var references and - * expressions which require the full ExecTargetList machinery. To be a - * simple Var, a Var has to be a user attribute and not mismatch the - * inputDesc. (Note: if there is a type mismatch then ExecEvalScalarVar - * will probably throw an error at runtime, but we leave that to it.) - */ + /* + * 我们将目标列表元素分为简单的 Var 引用和需要完整的 ExecTargetList 机制的表达式。 + * 要成为简单的 Var,Var 必须是用户属性并且不与 inputDesc 不匹配。 + * (注意:如果存在类型不匹配,则 ExecEvalScalarVar 可能会在运行时引发错误,但我们将其交给它处理。) + */ exprlist = NIL; numSimpleVars = 0; directMap = true; @@ -835,7 +793,7 @@ ProjectionInfo* ExecBuildProjectionInfo( } else { /* Not a simple variable, add it to generic targetlist */ exprlist = lappend(exprlist, gstate); - /* Examine expr to include contained Vars in lastXXXVar counts */ + /* 检查表达式以包含在 lastXXXVar 计数中包含的变量 */ get_last_attnums((Node*)variable, projInfo); } } @@ -852,10 +810,9 @@ ProjectionInfo* ExecBuildProjectionInfo( } /* - * get_last_attnums: expression walker for ExecBuildProjectionInfo + * get_last_attnums: 用于 ExecBuildProjectionInfo 的表达式遍历器 * - * Update the lastXXXVar counts to be at least as large as the largest - * attribute numbers found in the expression + * 更新 lastXXXVar 计数,使其至少与表达式中发现的最大属性号一样大 */ static bool get_last_attnums(Node* node, ProjectionInfo* projInfo) { @@ -885,12 +842,10 @@ static bool get_last_attnums(Node* node, ProjectionInfo* projInfo) return false; } - /* - * Don't examine the arguments of Aggrefs or WindowFuncs, because those do - * not represent expressions to be evaluated within the overall - * overall targetlist's econtext. GroupingFunc arguments are never - * evaluated at all. - */ + /* + * 不要检查 Aggrefs 或 WindowFuncs 的参数,因为它们不表示在整体目标列表的 econtext 中要评估的表达式。 + * GroupingFunc 参数根本不会被评估。 + */ if (IsA(node, Aggref) || IsA(node, GroupingFunc)) return false; if (IsA(node, WindowFunc)) @@ -898,55 +853,37 @@ static bool get_last_attnums(Node* node, ProjectionInfo* projInfo) return expression_tree_walker(node, (bool (*)())get_last_attnums, (void*)projInfo); } -/* ---------------- - * ExecAssignProjectionInfo +/* + * 从节点的目标列表中形成投影信息 * - * forms the projection information from the node's targetlist - * - * Notes for inputDesc are same as for ExecBuildProjectionInfo: supply it - * for a relation-scan node, can pass NULL for upper-level nodes - * ---------------- + * 输入 inputDesc 的注意事项与 ExecBuildProjectionInfo 相同:为关系扫描节点提供它,对于上层节点可以传递 NULL */ + void ExecAssignProjectionInfo(PlanState* planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = ExecBuildProjectionInfo( planstate->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, inputDesc); } -/* ---------------- - * ExecFreeExprContext +/* + * 在执行器关闭时,需要显式地释放计划节点的 ExprContext,因为可能有需要调用的关闭回调函数。(上述例程创建的其他资源,如投影信息,不需要显式释放,因为它们只是在每个查询的内存上下文中的内存。) * - * A plan node's ExprContext should be freed explicitly during executor - * shutdown because there may be shutdown callbacks to call. (Other resources - * made by the above routines, such as projection info, don't need to be freed - * explicitly because they're just memory in the per-query memory context.) - * - * However ... there is no particular need to do it during ExecEndNode, - * because FreeExecutorState will free any remaining ExprContexts within - * the EState. Letting FreeExecutorState do it allows the ExprContexts to - * be freed in reverse order of creation, rather than order of creation as - * will happen if we delete them here, which saves O(N^2) work in the list - * cleanup inside FreeExprContext. - * ---------------- + * 然而... 没有特定的需要在 ExecEndNode 期间执行它,因为 FreeExecutorState 将在 EState 中释放所有剩余的 ExprContext。让 FreeExecutorState 执行它允许 ExprContexts 按创建的相反顺序进行释放,而不是按创建顺序进行释放,这可以节省在 FreeExprContext 内部的列表清理中的 O(N^2) 的工作。 */ + void ExecFreeExprContext(PlanState* planstate) { - /* - * Per above discussion, don't actually delete the ExprContext. We do - * unlink it from the plan node, though. - */ + /* + * 根据上述讨论,实际上不要删除 ExprContext。但我们会将其从计划节点中取消链接。 + */ planstate->ps_ExprContext = NULL; } -/* ---------------------------------------------------------------- - * the following scan type support functions are for - * those nodes which are stubborn and return tuples in - * their Scan tuple slot instead of their Result tuple - * slot.. luck fur us, these nodes do not do projections - * so we don't have to worry about getting the ProjectionInfo - * right for them... -cim 6/3/91 - * ---------------------------------------------------------------- +/* + * 以下的扫描类型支持函数是为了那些顽固的节点,它们将元组返回到它们的扫描元组槽而不是结果元组槽中。 + * 幸运的是,这些节点不进行投影操作,所以我们不必担心为它们正确获取 ProjectionInfo。 -cim 6/3/91 */ + /* ---------------- * ExecGetScanType * ---------------- @@ -991,8 +928,7 @@ void ExecAssignScanTypeFromOuterPlan(ScanState* scanstate) /* ---------------------------------------------------------------- * ExecRelationIsTargetRelation * - * Detect whether a relation (identified by rangetable index) - * is one of the target relations of the query. + * 检测一个关系(通过范围表索引标识)是否是查询的目标关系之一。 * ---------------------------------------------------------------- */ bool ExecRelationIsTargetRelation(EState* estate, Index scanrelid) @@ -1008,16 +944,11 @@ bool ExecRelationIsTargetRelation(EState* estate, Index scanrelid) return false; } -/* ---------------------------------------------------------------- - * ExecOpenScanRelation +/* ExecOpenScanRelation + + * 在基本级别的扫描计划节点要扫描的堆关系上打开扫描。应该在节点的ExecInit例程中调用此函数。 * - * Open the heap relation to be scanned by a base-level scan plan node. - * This should be called during the node's ExecInit routine. - * - * By default, this acquires AccessShareLock on the relation. However, - * if the relation was already locked by InitPlan, we don't need to acquire - * any additional lock. This saves trips to the shared lock manager. - * ---------------------------------------------------------------- + * 默认情况下,这会在关系上获取AccessShareLock。但是,如果关系已经被InitPlan锁定,我们就不需要获取任何其他锁定。这可以节省共享锁管理器的访问。 */ Relation ExecOpenScanRelation(EState* estate, Index scanrelid) { @@ -1025,11 +956,11 @@ Relation ExecOpenScanRelation(EState* estate, Index scanrelid) LOCKMODE lockmode; Relation rel; - /* - * Determine the lock type we need. First, scan to see if target relation - * is a result relation. If not, check if it's a FOR UPDATE/FOR SHARE - * relation. In either of those cases, we got the lock already. - */ + /* + * 确定我们需要的锁定类型。首先,扫描以查看目标关系是否是结果关系。 + * 如果不是,检查它是否是一个FOR UPDATE/FOR SHARE关系。 + * 在这两种情况下,我们已经获取了锁定。 + */ lockmode = AccessShareLock; if (ExecRelationIsTargetRelation(estate, scanrelid)) lockmode = NoLock; @@ -1039,7 +970,7 @@ Relation ExecOpenScanRelation(EState* estate, Index scanrelid) foreach (l, estate->es_rowMarks) { ExecRowMark* erm = (ExecRowMark*)lfirst(l); - /* Keep this check in sync with InitPlan! */ + /* 保持这个检查与InitPlan同步! */ if (erm->rti == scanrelid && erm->relation != NULL) { lockmode = NoLock; break; @@ -1047,12 +978,12 @@ Relation ExecOpenScanRelation(EState* estate, Index scanrelid) } } - /* Open the relation and acquire lock as needed */ + /* 打开关系并根据需要获取锁定 */ reloid = getrelid(scanrelid, estate->es_range_table); rel = heap_open(reloid, lockmode); if (STMT_RETRY_ENABLED) { - // do noting for now, if query retry is on, just to skip validateTempRelation here + // 目前什么都不做,如果查询重试已启用,则跳过在这里进行 validateTempRelation 操作 } else validateTempRelation(rel); @@ -1062,17 +993,17 @@ Relation ExecOpenScanRelation(EState* estate, Index scanrelid) /* ---------------------------------------------------------------- * ExecCloseScanRelation * - * Close the heap relation scanned by a base-level scan plan node. - * This should be called during the node's ExecEnd routine. + * 关闭由基础级扫描计划节点扫描的堆关系。 + * 应该在节点的 ExecEnd 例程中调用此函数。 * - * Currently, we do not release the lock acquired by ExecOpenScanRelation. - * This lock should be held till end of transaction. (There is a faction - * that considers this too much locking, however.) + * 目前,我们不会释放由 ExecOpenScanRelation 获取的锁。 + * 这个锁应该保持到事务结束。(有人认为这是过多的锁定,但也有人持相反意见。) * - * If we did want to release the lock, we'd have to repeat the logic in - * ExecOpenScanRelation in order to figure out what to release. + * 如果我们确实想要释放这个锁,我们需要重复 ExecOpenScanRelation 中的逻辑, + * 以便弄清楚需要释放哪些资源。 * ---------------------------------------------------------------- */ + void ExecCloseScanRelation(Relation scanrel) { heap_close(scanrel, NoLock); @@ -1080,14 +1011,14 @@ void ExecCloseScanRelation(Relation scanrel) /* * @@GaussDB@@ - * Target : data partition - * Brief : Open the heap partition to be scanned by a base-level scan plan - * : node. This should be called during the node's ExecInit routine. - * Description : - * Notes : By default, this acquires AccessShareLock on the partitioned relation. - * : However, if the relation was already locked by InitPlan, we don't need - * : to acquire any additional lock. This saves trips to the shared lock manager. + * 目标:数据分区 + * 简述:打开由基础级扫描计划节点扫描的堆分区关系。这应该在节点的 ExecInit 例程中调用。 + * 描述: + * 注意:默认情况下,这会在分区关系上获取 AccessShareLock。 + * 但是,如果关系已经被 InitPlan 锁定,我们就不需要获取任何额外的锁。 + * 这可以减少到共享锁管理器的访问次数。 */ + Partition ExecOpenScanParitition(EState* estate, Relation parent, PartitionIdentifier* partID, LOCKMODE lockmode) { Oid partoid = InvalidOid; @@ -1096,7 +1027,7 @@ Partition ExecOpenScanParitition(EState* estate, Relation parent, PartitionIdent Assert(PointerIsValid(parent)); Assert(PointerIsValid(partID)); - /* OK, open the relation and acquire lock as needed */ + /* 打开关系并根据需要获取锁 */ partoid = partIDGetPartOid(parent, partID); return partitionOpen(parent, partoid, lockmode);