OpenGuass开源代码评注赛-针对Executor的代码评注 #35

Closed
TerryTongJ wants to merge 31 commits from TerryTongJ/openGauss-server:master into master
1 changed files with 277 additions and 257 deletions
Showing only changes of commit 8a28dcc5dd - Show all commits

View File

@ -1524,7 +1524,7 @@ void InitPlan(QueryDesc *queryDesc, int eflags)
if (plannedstmt->num_streams > 0 && !StreamThreadAmI() &&
!(eflags & EXEC_FLAG_EXPLAIN_ONLY)) {
/* init stream thread in parallel */
/* 在并行中初始化流线程 */
StartUpStreamInParallel(queryDesc->plannedstmt, queryDesc->estate);
}
@ -2062,11 +2062,12 @@ static void ExecCollectMaterialForSubplan(EState *estate)
foreach (lc, estate->es_material_of_subplan) {
PlanState *node = (PlanState *)lfirst(lc);
/*
* If the current materliaze node is recursive-union and the right tree has stream
* node, we are skip the pre-materliaze the subplan as at current point the SyncPoint
* on consumer side is not start yet in ExecRecursiveUnion()
*/
/*
*
*
* ExecRecursiveUnion()
*/
if (EXEC_IN_RECURSIVE_MODE(node->plan)) {
continue;
}
@ -2075,13 +2076,15 @@ static void ExecCollectMaterialForSubplan(EState *estate)
for (;;) {
TupleTableSlot *slot = NULL;
/* Reset the per-output-tuple exprcontext each time */
/* 每次重置输出元组的表达式上下文 */
ResetPerTupleExprContext(estate);
slot = ExecProcNode(node);
if (TupIsNull(slot)) {
/* Reset Material so that its output can be re-scanned */
/* 重置物化操作,以便可以重新扫描其输出 */
ExecReScan(node);
break;
}
@ -2090,13 +2093,14 @@ static void ExecCollectMaterialForSubplan(EState *estate)
for (;;) {
VectorBatch *batch = NULL;
/*
* Execute the plan and obtain a batch
*/
/*
*
*/
batch = VectorEngine(node);
if (BatchIsNull(batch)) {
/* Reset Material so that its output can be re-scanned */
/* 重置物化操作,以便可以重新扫描其输出 */
VecExecReScan(node);
break;
}
@ -2108,15 +2112,14 @@ static void ExecCollectMaterialForSubplan(EState *estate)
/* ----------------------------------------------------------------
* ExecutePlan
*
* Processes the query plan until we have retrieved 'numberTuples' tuples,
* moving in the specified direction.
* 'numberTuples'
*
* Runs to completion if numberTuples is 0
* numberTuples 0
*
* Note: the ctid attribute is a 'junk' attribute that is removed before the
* user can see it
* ctid 'junk'
* ----------------------------------------------------------------
*/
#ifdef ENABLE_MOT
static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation, bool sendTuples, long numberTuples,
ScanDirection direction, DestReceiver *dest, JitExec::JitContext* motJitContext)
@ -2134,30 +2137,32 @@ static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation,
bool motFinishedExecution = false;
#endif
/* Mark sync-up step is required */
/* 标记需要同步步骤 */
if (NeedSyncUpProducerStep(planstate->plan)) {
need_sync_step = true;
/*
* (G)Distributed With-Recursive Support
*
* If current producer thread is under a recursive cte plan node, we need do
* step sync-up across the whole cluster
*/
/*
* G
*
* 线CTE计划节点下
*/
u_sess->exec_cxt.global_iteration = 0;
ExecutePlanSyncProducer(planstate, WITH_RECURSIVE_SYNC_NONERQ, &recursive_early_stop, &current_tuple_count);
u_sess->exec_cxt.global_iteration = 1;
}
/*
* Set the direction.
*/
/*
*
*/
estate->es_direction = direction;
if (IS_PGXC_DATANODE) {
/* Collect Material for Subplan first */
/* 首先收集子查询计划的物化数据 */
ExecCollectMaterialForSubplan(estate);
/* Collect Executor run time including sending data time */
/* 收集执行器运行时间,包括发送数据所用时间 */
if (estate->es_instrument != INSTRUMENT_NONE && u_sess->instr_cxt.global_instr &&
u_sess->instr_cxt.thread_instr) {
stream_instrument = true;
@ -2166,21 +2171,24 @@ static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation,
}
}
/* Change DestReceiver's tmpContext to PerTupleMemoryContext to avoid memory leak. */
/* 将 DestReceiver 的 tmpContext 更改为 PerTupleMemoryContext以避免内存泄漏。 */
dest->tmpContext = GetPerTupleMemoryContext(estate);
// planstate->plan will be release if rollback excuted
// 如果执行回滚planstate->plan 将被释放
bool is_saved_recursive_union_plan_nodeid = EXEC_IN_RECURSIVE_MODE(planstate->plan);
/*
* Loop until we've processed the proper number of tuples from the plan.
*/
/*
*
*/
for (;;) {
/* Reset the per-output-tuple exprcontext */
/* 重置每个输出元组的表达式上下文 */
ResetPerTupleExprContext(estate);
/*
* Execute the plan and obtain a tuple
*/
/*
*
*/
#ifdef ENABLE_MOT
if (unlikely(recursive_early_stop)) {
slot = NULL;
@ -2188,13 +2196,13 @@ static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation,
// MOT LLVM
int scanEnded = 0;
if (!motFinishedExecution) {
// previous iteration has not signaled end of scan
// 前一次迭代尚未标志着扫描结束
slot = planstate->ps_ResultTupleSlot;
uint64_t tuplesProcessed = 0;
int rc = JitExec::JitExecQuery(
motJitContext, estate->es_param_list_info, slot, &tuplesProcessed, &scanEnded);
if (scanEnded || (tuplesProcessed == 0) || (rc != 0)) {
// raise flag so that next round we will bail out (current tuple still must be reported to user)
// 设置标志,以便在下一轮迭代中退出(当前元组仍然必须向用户报告)
motFinishedExecution = true;
}
} else {
@ -2207,24 +2215,25 @@ static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation,
slot = unlikely(recursive_early_stop) ? NULL : ExecProcNode(planstate);
#endif
/*
* ------------------------------------------------------------------------------
* (G)Distributed With-Recursive Support
*
* If under recursive cte, we need check sync step and do rescan properly
*/
/*
* ------------------------------------------------------------------------------
* G
*
* CTE下
*/
if (unlikely(need_sync_step) && TupIsNull(slot)) {
if (!ExecutePlanSyncProducer(planstate, WITH_RECURSIVE_SYNC_RQSTEP, &recursive_early_stop,
&current_tuple_count)) {
/* current iteration step is not finish, continue to the next iteration */
/* 当前迭代步骤尚未完成,继续到下一次迭代 */
continue;
}
}
/*
* if the tuple is null, then we assume there is nothing more to
* process so we just end the loop...
/*
* null...
*/
if (TupIsNull(slot)) {
if(!is_saved_recursive_union_plan_nodeid) {
break;
@ -2233,24 +2242,25 @@ static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation,
break;
}
/*
* If we have a junk filter, then project a new tuple with the junk
* removed.
/*
*
*
* Store this new "clean" tuple in the junkfilter's resultSlot.
* (Formerly, we stored it back over the "dirty" tuple, which is WRONG
* because that tuple slot has the wrong descriptor.)
* resultSlot
*
*/
#ifdef ENABLE_MULTIPLE_NDOES
if (estate->es_junkFilter != NULL && !StreamTopConsumerAmI() && !StreamThreadAmI()) {
#else
if (estate->es_junkFilter != NULL && !StreamThreadAmI()) {
#endif
/* If junkfilter->jf_resultSlot->tts_tupleDescriptor is different from slot->tts_tupleDescriptor,
* and the datatype is not Compatible,
* we reset junkfilter->jf_resultSlot->tts_tupleDescriptor by slot->tts_tupleDescriptor.
* This just do only once.
*/
/*
* junkfilter->jf_resultSlot->tts_tupleDescriptor slot->tts_tupleDescriptor
*
* junkfilter->jf_resultSlot->tts_tupleDescriptor slot->tts_tupleDescriptor
*
*/
if (current_tuple_count == 0) {
ExecSetjunkFilteDescriptor(estate->es_junkFilter, slot->tts_tupleDescriptor);
}
@ -2262,10 +2272,10 @@ static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation,
t_thrd.pgxc_cxt.GlobalNetInstr = planstate->instrument;
}
#endif
/*
* If we are supposed to send the tuple somewhere, do so. (In
* practice, this is probably always the case at this point.)
*/
/*
*
*/
#ifdef ENABLE_MULTIPLE_NDOES
if (sendTuples && !u_sess->exec_cxt.executorStopFlag)
#else
@ -2278,29 +2288,28 @@ static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation,
#ifdef ENABLE_MULTIPLE_NDOES
t_thrd.pgxc_cxt.GlobalNetInstr = NULL;
#endif
/*
* Count tuples processed, if this is a SELECT. (For other operation
* types, the ModifyTable plan node must count the appropriate
* events.)
/*
* SELECT语句ModifyTable计划节点必须计算相应的事件
*/
if (operation == CMD_SELECT) {
(estate->es_processed)++;
}
/*
* check our tuple count.. if we've processed the proper number then
* quit, else loop again and process more tuples. Zero numberTuples
* means no limit.
*/
/*
* ..退numberTuples为零表示没有限制
*/
current_tuple_count++;
if (numberTuples == current_tuple_count) {
break;
}
}
/*
* if current plan is working for expression, no need to collect instrumentation.
*/
/*
*
*/
if (estate->es_instrument != INSTRUMENT_NONE && u_sess->instr_cxt.global_instr && StreamTopConsumerAmI() &&
u_sess->instr_cxt.thread_instr) {
int64 peak_memory = (uint64)(t_thrd.shemem_ptr_cxt.mySessionMemoryEntry->peakChunksQuery -
@ -2313,15 +2322,14 @@ static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation,
/* ----------------------------------------------------------------
* ExecutePlan
*
* Processes the query plan until we have retrieved 'numberTuples' tuples,
* moving in the specified direction.
* 'numberTuples'
*
* Runs to completion if numberTuples is 0
* numberTuples 0
*
* Note: the ctid attribute is a 'junk' attribute that is removed before the
* user can see it
* ctid 'junk'
* ----------------------------------------------------------------
*/
static void ExecuteVectorizedPlan(EState *estate, PlanState *planstate, CmdType operation, bool sendTuples,
long numberTuples, ScanDirection direction, DestReceiver *dest)
{
@ -2329,57 +2337,61 @@ static void ExecuteVectorizedPlan(EState *estate, PlanState *planstate, CmdType
long current_tuple_count;
bool stream_instrument = false;
/*
* initialize local variables
*/
/*
*
*/
current_tuple_count = 0;
/*
* Set the direction.
*/
/*
*
*/
estate->es_direction = direction;
if (IS_PGXC_DATANODE) {
/* Collect Executor run time including sending data time */
/* 收集执行器运行时间,包括发送数据所用时间 */
if (estate->es_instrument != INSTRUMENT_NONE && u_sess->instr_cxt.global_instr) {
stream_instrument = true;
int plan_id = planstate->plan->plan_node_id;
u_sess->instr_cxt.global_instr->SetStreamSend(plan_id, true);
}
/* Collect Material for Subplan first */
/* 首先收集子查询计划的物化数据 */
ExecCollectMaterialForSubplan(estate);
}
/*
* Loop until we've processed the proper number of tuples from the plan.
*/
/*
*
*/
for (;;) {
/* Reset the per-output-tuple exprcontext */
/* 重置每个输出元组的表达式上下文 */
ResetPerTupleExprContext(estate);
/*
* Execute the plan and obtain a tuple
*/
/*
*
*/
batch = VectorEngine(planstate);
/*
* if the tuple is null, then we assume there is nothing more to
* process so we just end the loop...
*/
/*
* null...
*/
if (BatchIsNull(batch)) {
ExecEarlyFree(planstate);
break;
}
/*
* If we have a junk filter, then project a new tuple with the junk
* removed.
/*
*
*
* Store this new "clean" tuple in the junkfilter's resultSlot.
* (Formerly, we stored it back over the "dirty" tuple, which is WRONG
* because that tuple slot has the wrong descriptor.)
* resultSlot
*
*/
#ifdef ENABLE_MULTIPLE_NDOES
if (estate->es_junkFilter != NULL && !StreamTopConsumerAmI() && !StreamThreadAmI()) {
#else
@ -2393,39 +2405,38 @@ static void ExecuteVectorizedPlan(EState *estate, PlanState *planstate, CmdType
t_thrd.pgxc_cxt.GlobalNetInstr = planstate->instrument;
}
/*
* If we are supposed to send the tuple somewhere, do so. (In
* practice, this is probably always the case at this point.)
*/
/*
*
*/
if (sendTuples && !u_sess->exec_cxt.executorStopFlag) {
(*dest->sendBatch)(batch, dest);
}
t_thrd.pgxc_cxt.GlobalNetInstr = NULL;
/*
* Count tuples processed, if this is a SELECT. (For other operation
* types, the ModifyTable plan node must count the appropriate
* events.)
*/
/*
* SELECT语句ModifyTable计划节点必须计算相应的事件
*/
if (operation == CMD_SELECT) {
estate->es_processed += batch->m_rows;
}
/*
* check our tuple count.. if we've processed the proper number then
* quit, else loop again and process more tuples. Zero numberTuples
* means no limit.
*/
/*
* ..退numberTuples为零表示没有限制
*/
current_tuple_count += batch->m_rows;
if (numberTuples && numberTuples == current_tuple_count) {
break;
}
}
/*
* if current plan is working for expression, no need to collect instrumentation.
*/
/*
*
*/
if (estate->es_instrument != INSTRUMENT_NONE && u_sess->instr_cxt.global_instr && StreamTopConsumerAmI()) {
int64 peak_memory = (uint64)(t_thrd.shemem_ptr_cxt.mySessionMemoryEntry->peakChunksQuery -
t_thrd.shemem_ptr_cxt.mySessionMemoryEntry->initMemInChunks)
@ -2435,8 +2446,9 @@ static void ExecuteVectorizedPlan(EState *estate, PlanState *planstate, CmdType
}
/*
* ExecRelCheck --- check that tuple meets constraints for result relation
* ExecRelCheck ---
*/
static const char *ExecRelCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
{
Relation rel = resultRelInfo->ri_RelationDesc;
@ -2447,40 +2459,41 @@ static const char *ExecRelCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *sl
List *qual = NIL;
int i;
/*
* If first time through for this result relation, build expression
* nodetrees for rel's constraint expressions. Keep them in the per-query
* memory context so they'll survive throughout the query.
*/
/*
*
* 便
*/
if (resultRelInfo->ri_ConstraintExprs == NULL) {
oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
resultRelInfo->ri_ConstraintExprs = (List **)palloc(ncheck * sizeof(List *));
for (i = 0; i < ncheck; i++) {
/* ExecQual wants implicit-AND form */
/* ExecQual 需要隐式的 AND 形式 */
qual = make_ands_implicit((Expr *)stringToNode(check[i].ccbin));
resultRelInfo->ri_ConstraintExprs[i] = (List *)ExecPrepareExpr((Expr *)qual, estate);
}
(void)MemoryContextSwitchTo(oldContext);
}
/*
* We will use the EState's per-tuple context for evaluating constraint
* expressions (creating it if it's not already there).
*/
/*
* 使 EState
*/
econtext = GetPerTupleExprContext(estate);
/* Arrange for econtext's scan tuple to be the tuple under test */
/* 确保 econtext 的扫描元组是待测试的元组 */
econtext->ecxt_scantuple = slot;
/* And evaluate the constraints */
/* 然后评估约束条件 */
for (i = 0; i < ncheck; i++) {
qual = resultRelInfo->ri_ConstraintExprs[i];
/*
* NOTE: SQL92 specifies that a NULL result from a constraint
* expression is not to be treated as a failure. Therefore, tell
* ExecQual to return TRUE for NULL.
*/
/*
* SQL92规定NULL结果不应视为失败ExecQual对于NULL返回TRUE
*/
if (!ExecQual(qual, econtext, true)) {
return check[i].ccname;
}
@ -2492,6 +2505,7 @@ static const char *ExecRelCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *sl
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
{
// 获取关系描述符和约束信息
Relation rel = resultRelInfo->ri_RelationDesc;
TupleDesc tupdesc = RelationGetDescr(rel);
TupleConstr *constr = tupdesc->constr;
@ -2500,28 +2514,26 @@ void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState
Bitmapset *updatedCols = NULL;
int maxfieldlen = 64;
// 断言确保约束信息存在
Assert(constr);
/* Get the Table Accessor Method*/
Assert(slot != NULL && slot->tts_tupleDescriptor != NULL);
// 检查是否有 NOT NULL 约束,并处理
if (constr->has_not_null) {
int natts = tupdesc->natts;
int attrChk;
for (attrChk = 1; attrChk <= natts; attrChk++) {
// 如果属性为 NOT NULL 且当前元组中的值为空,则报错
if (tupdesc->attrs[attrChk - 1]->attnotnull && tableam_tslot_attisnull(slot, attrChk)) {
char *val_desc = NULL;
bool rel_masked = u_sess->attr.attr_security.Enable_Security_Policy &&
is_masked_relation_enabled(RelationGetRelid(rel));
// 获取插入和更新的列集合,并构建错误信息
insertedCols = GetInsertedColumns(resultRelInfo, estate);
updatedCols = GetUpdatedColumns(resultRelInfo, estate);
modifiedCols = bms_union(insertedCols, updatedCols);
if (!rel_masked) {
val_desc =
ExecBuildSlotValueDescription(RelationGetRelid(rel), slot, tupdesc, modifiedCols, maxfieldlen);
}
// 构建包含错误列值的错误描述
char *val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel), slot, tupdesc, modifiedCols, maxfieldlen);
// 报告 NOT NULL 约束违反错误
ereport(ERROR, (errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("null value in column \"%s\" violates not-null constraint",
NameStr(tupdesc->attrs[attrChk - 1]->attname)),
@ -2530,25 +2542,24 @@ void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState
}
}
// 检查其他约束条件,并处理
if (constr->num_check == 0) {
return;
}
// 检查 CHECK 约束条件,并返回失败的 CHECK 约束
const char *failed = ExecRelCheck(resultRelInfo, slot, estate);
if (failed == NULL) {
return;
}
char *val_desc = NULL;
bool rel_masked = u_sess->attr.attr_security.Enable_Security_Policy &&
is_masked_relation_enabled(RelationGetRelid(rel));
// 获取插入和更新的列集合,并构建错误信息
insertedCols = GetInsertedColumns(resultRelInfo, estate);
updatedCols = GetUpdatedColumns(resultRelInfo, estate);
modifiedCols = bms_union(insertedCols, updatedCols);
if (!rel_masked) {
val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel), slot, tupdesc, modifiedCols, maxfieldlen);
}
/* client_min_messages < NOTICE show error details. */
char *val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel), slot, tupdesc, modifiedCols, maxfieldlen);
// 根据 client_min_messages 设置,报告 CHECK 约束违反错误
if (client_min_messages < NOTICE) {
ereport(ERROR,
(errmodule(MOD_EXECUTOR), errcode(ERRCODE_CHECK_VIOLATION),
@ -2569,24 +2580,18 @@ void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState
}
/*
* ExecBuildSlotValueDescription -- construct a string representing a tuple
* ExecBuildSlotValueDescription --
*
* This is intentionally very similar to BuildIndexValueDescription, but
* unlike that function, we truncate long field values (to at most maxfieldlen
* bytes). That seems necessary here since heap field values could be very
* long, whereas index entries typically aren't so wide.
* BuildIndexValueDescription maxfieldlen字节
*
*
* Also, unlike the case with index entries, we need to be prepared to ignore
* dropped columns. We used to use the slot's tuple descriptor to decode the
* data, but the slot's descriptor doesn't identify dropped columns, so we
* now need to be passed the relation's descriptor.
* 使
*
*
* Note that, like BuildIndexValueDescription, if the user does not have
* permission to view any of the columns involved, a NULL is returned. Unlike
* BuildIndexValueDescription, if the user has access to view a subset of the
* column involved, that subset will be returned with a key identifying which
* columns they are.
* BuildIndexValueDescription类似NULL
* BuildIndexValueDescription不同的是
*/
char *ExecBuildSlotValueDescription(Oid reloid, TupleTableSlot *slot, TupleDesc tupdesc, Bitmapset *modifiedCols,
int maxfieldlen)
{
@ -2604,25 +2609,25 @@ char *ExecBuildSlotValueDescription(Oid reloid, TupleTableSlot *slot, TupleDesc
appendStringInfoChar(&buf, '(');
/*
* Check if the user has permissions to see the row. Table-level SELECT
* allows access to all columns. If the user does not have table-level
* SELECT then we check each column and include those the user has SELECT
* rights on. Additionally, we always include columns the user provided
* data for.
*/
/*
* SELECT允许访问所有列SELECT权限
* SELECT权限的列
*/
aclresult = pg_class_aclcheck(reloid, GetUserId(), ACL_SELECT);
if (aclresult != ACLCHECK_OK) {
/* Set up the buffer for the column list */
/* 为列列表设置缓冲区 */
initStringInfo(&collist);
appendStringInfoChar(&collist, '(');
} else {
table_perm = any_perm = true;
}
/* Make sure the tuple is fully deconstructed */
/* 确保元组已完全解构 */
/* 获取表的访问器方法 */
/* Get the Table Accessor Method*/
Assert(slot != NULL && slot->tts_tupleDescriptor != NULL);
tableam_tslot_getallattrs(slot);
@ -2631,18 +2636,18 @@ char *ExecBuildSlotValueDescription(Oid reloid, TupleTableSlot *slot, TupleDesc
char *val = NULL;
int vallen;
/* ignore dropped columns */
/* 忽略被删除的列 */
if (tupdesc->attrs[i]->attisdropped) {
continue;
}
if (!table_perm) {
/*
* No table-level SELECT, so need to make sure they either have
* SELECT rights on the column or that they have provided the
* data for the column. If not, omit this column from the error
* message.
*/
/*
* SELECT权限SELECT权限
*
*/
aclresult = pg_attribute_aclcheck(reloid, tupdesc->attrs[i]->attnum, GetUserId(), ACL_SELECT);
if (bms_is_member(tupdesc->attrs[i]->attnum - FirstLowInvalidHeapAttributeNumber, modifiedCols) ||
aclresult == ACLCHECK_OK) {
@ -2675,7 +2680,8 @@ char *ExecBuildSlotValueDescription(Oid reloid, TupleTableSlot *slot, TupleDesc
write_comma = true;
}
/* truncate if needed */
/* 如果需要,进行截断 */
vallen = strlen(val);
if (vallen <= maxfieldlen) {
appendStringInfoString(&buf, val);
@ -2687,7 +2693,8 @@ char *ExecBuildSlotValueDescription(Oid reloid, TupleTableSlot *slot, TupleDesc
}
}
/* If we end up with zero columns being returned, then return NULL. */
/* 如果最终没有返回任何列则返回NULL。 */
if (!any_perm) {
return NULL;
}
@ -2705,8 +2712,9 @@ char *ExecBuildSlotValueDescription(Oid reloid, TupleTableSlot *slot, TupleDesc
}
/*
* ExecFindRowMark -- find the ExecRowMark struct for given rangetable index
* ExecFindRowMark -- ExecRowMark结构
*/
ExecRowMark *ExecFindRowMark(EState *estate, Index rti)
{
ListCell *lc = NULL;
@ -2724,12 +2732,12 @@ ExecRowMark *ExecFindRowMark(EState *estate, Index rti)
}
/*
* ExecBuildAuxRowMark -- create an ExecAuxRowMark struct
* ExecBuildAuxRowMark -- ExecAuxRowMark结构
*
* Inputs are the underlying ExecRowMark struct and the targetlist of the
* input plan node (not planstate node!). We need the latter to find out
* the column numbers of the resjunk columns.
* ExecRowMark结构和输入计划节点的目标列表
*
*/
ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *)palloc0(sizeof(ExecAuxRowMark));
@ -2738,11 +2746,13 @@ ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
aerm->rowmark = erm;
/* Look up the resjunk columns associated with this rowmark */
/* 查找与此行标记关联的结果冗余列 */
if (erm->relation) {
Assert(erm->markType != ROW_MARK_COPY);
/* if child rel, need tableoid */
/* 如果是子关系需要表OID */
if (erm->rti != erm->prti || RelationIsPartitioned(erm->relation)) {
rc = snprintf_s(resname, sizeof(resname), sizeof(resname) - 1, "tableoid%u", erm->rowmarkId);
securec_check_ss(rc, "\0", "\0");
@ -2763,7 +2773,8 @@ ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
errmsg("could not find bucketid junk %s column when build RowMark", resname)));
}
}
/* always need ctid for real relations */
/* 对于真实关系始终需要ctid */
rc = snprintf_s(resname, sizeof(resname), sizeof(resname) - 1, "ctid%u", erm->rowmarkId);
securec_check_ss(rc, "\0", "\0");
@ -2788,93 +2799,110 @@ ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
return aerm;
}
/*
* EvalPlanQualUHeap -
*
* estateEState结构
* epqstateEPQState结构
* relation
* rti
* tidtuple的ItemPointer
* priorXmaxXID
*
* TupleTableSlottuple的数据
*/
TupleTableSlot *EvalPlanQualUHeap(EState *estate, EPQState *epqstate, Relation relation, Index rti, ItemPointer tid,
TransactionId priorXmax)
{
TupleTableSlot *slot = NULL;
UHeapTuple copyTuple = NULL;
TupleTableSlot *slot = NULL;
UHeapTuple copyTuple = NULL;
// 断言确保范围表索引大于0
Assert(rti > 0);
copyTuple =
UHeapLockUpdated(estate->es_output_cid, relation, LockTupleExclusive, tid, priorXmax, estate->es_snapshot);
// 获取锁并获取更新后的tuple
copyTuple = UHeapLockUpdated(estate->es_output_cid, relation, LockTupleExclusive, tid, priorXmax, estate->es_snapshot);
// 如果找不到tuple返回NULL
if (copyTuple == NULL) {
return NULL;
}
// 断言确保获取的tuple为UHeap类型
Assert(copyTuple->tupTableType = UHEAP_TUPLE);
// 更新传入的tid为获取到的tuple的ctid
*tid = copyTuple->ctid;
// 开始处理过程化计划的限制条件
EvalPlanQualBegin(epqstate, estate);
// 将获取到的tuple设置到过程化计划限制条件状态中
EvalPlanQualSetTuple(epqstate, rti, copyTuple);
// 获取过程化计划的行标记信息
EvalPlanQualFetchRowMarks(epqstate);
// 执行过程化计划限制条件并获取符合条件的tuple的slot
slot = EvalPlanQualNext(epqstate);
// materialize the slot
// 将slot中的数据materialize即将slot中的数据转换为可见的tuple
if (!TupIsNull(slot)) {
ExecGetUHeapTupleFromSlot(slot);
}
// 将tuple从过程化计划限制条件状态中移除
EvalPlanQualSetTuple(epqstate, rti, NULL);
return slot;
}
/*
* EvalPlanQual logic --- recheck modified tuple(s) to see if we want to
* process the updated version under READ COMMITTED rules.
* EvalPlanQual逻辑 READ COMMITTED规则处理更新版本
*
* See gausskernel/runtime/executor/README for some info about how this works.
* gausskernel/runtime/executor/README
*
* Check a modified tuple to see if we want to process its updated version
* under READ COMMITTED rules.
* READ COMMITTED规则处理其更新版本
*
* estate - outer executor state data
* epqstate - state for EvalPlanQual rechecking
* relation - table containing tuple
* rti - rangetable index of table containing tuple
* lockmode - requested tuple lock mode
* *tid - t_ctid from the outdated tuple (ie, next updated version)
* priorXmax - t_xmax from the outdated tuple
* estate -
* epqstate - EvalPlanQual重新检查的状态
* relation -
* rti -
* lockmode -
* *tid - t_ctid
* priorXmax - t_xmax
*
* *tid is also an output parameter: it's modified to hold the TID of the
* latest version of the tuple (note this may be changed even on failure)
* *tid也是一个输出参数TID使
*
* Returns a slot containing the new candidate update/delete tuple, or
* NULL if we determine we shouldn't process the row.
* /NULL
*
* Note: properly, lockmode should be declared as enum LockTupleMode,
* but we use "int" to avoid having to include heapam.h in executor.h.
* lockmode应该被声明为枚举类型LockTupleMode
* 使"int"executor.h中引入heapam.h
*/
TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate, Relation relation, Index rti, int lockmode,
ItemPointer tid, TransactionId priorXmax, bool partRowMoveUpdate)
{
TupleTableSlot *slot = NULL;
Tuple copyTuple;
// 断言确保范围表索引大于0
Assert(rti > 0);
/*
* Get and lock the updated version of the row; if fail, return NULL.
* NULL
*/
copyTuple = tableam_tuple_lock_updated(estate->es_output_cid, relation, lockmode, tid, priorXmax,
estate->es_snapshot);
// 如果找不到tuple返回NULL
if (copyTuple == NULL) {
/*
* The tuple has been deleted or update in row movement case.
*
*
*/
if (partRowMoveUpdate) {
/*
* the may be a row movement update action which delete tuple from original
* partition and insert tuple to new partition or we can add lock on the tuple
* to be delete or updated to avoid throw exception.
*/
ereport(ERROR, (errcode(ERRCODE_TRANSACTION_ROLLBACK),
errmsg("partition table update conflict"),
errdetail("disable row movement of table can avoid this conflict")));
@ -2883,47 +2911,41 @@ TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate, Relation relati
}
/*
* For UPDATE/DELETE we have to return tid of actual row we're executing
* PQ for.
* UPDATE/DELETE操作TID
*/
*tid = ((HeapTuple)copyTuple)->t_self;
/*
* Need to run a recheck subquery. Initialize or reinitialize EPQ state.
* EPQ状态
*/
EvalPlanQualBegin(epqstate, estate);
/*
* Free old test tuple, if any, and store new tuple where relation's scan
* node will see it
*
*/
EvalPlanQualSetTuple(epqstate, rti, copyTuple);
/*
* Fetch any non-locked source rows
*
*/
EvalPlanQualFetchRowMarks(epqstate);
/*
* Run the EPQ query. We assume it will return at most one tuple.
* EPQ查询
*/
slot = EvalPlanQualNext(epqstate);
/*
* If we got a tuple, force the slot to materialize the tuple so that it
* is not dependent on any local state in the EPQ query (in particular,
* it's highly likely that the slot contains references to any pass-by-ref
* datums that may be present in copyTuple). As with the next step, this
* is to guard against early re-use of the EPQ query.
* slot将其materialize便EPQ查询中的任何本地状态
* slot中高度可能包含在copyTuple中存在的传址引用数据
* EPQ查询的早期重用
*/
if (!TupIsNull(slot)) {
(void)tableam_tslot_get_tuple_from_slot(relation, slot);
}
/*
* Clear out the test tuple. This is needed in case the EPQ query is
* re-used to test a tuple for a different relation. (Not clear that can
* really happen, but let's be safe.)
* EPQ查询被重用以测试不同关系的元组
*/
EvalPlanQualSetTuple(epqstate, rti, NULL);
@ -2931,22 +2953,20 @@ TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate, Relation relati
}
/*
* Fetch a copy of the newest version of an outdated tuple
*
*
* cid - command ID
* relation - table containing tuple
* lockmode - requested tuple lock mode
* *tid - t_ctid from the outdated tuple (ie, next updated version)
* priorXmax - t_xmax from the outdated tuple
* cid - ID
* relation -
* lockmode -
* *tid - t_ctid
* priorXmax - t_xmax
*
* Returns a palloc'd copy of the newest tuple version, or NULL if we find
* that there is no newest version (ie, the row was deleted not updated).
* If successful, we have locked the newest tuple version, so caller does not
* need to worry about it changing anymore.
* palloc'd副本NULL
*
*
* Note: properly, lockmode should be declared as enum LockTupleMode,
* but we use "int" to avoid having to include heapam.h in executor.h.
* lockmode应该被声明为枚举类型LockTupleMode使"int"executor.h中引入heapam.h
*/
HeapTuple heap_lock_updated(CommandId cid, Relation relation, int lockmode, ItemPointer tid, TransactionId priorXmax)
{
HeapTuple copyTuple = NULL;