From f1a9e2ac18860286cedc0042e6f84da2f04cf9a7 Mon Sep 17 00:00:00 2001 From: LYLlyl Date: Tue, 5 Sep 2023 20:59:57 +0800 Subject: [PATCH] Update execUtils.cpp --- .../runtime/executor/execUtils.cpp | 572 +++++++++--------- 1 file changed, 283 insertions(+), 289 deletions(-) diff --git a/src/gausskernel/runtime/executor/execUtils.cpp b/src/gausskernel/runtime/executor/execUtils.cpp index e6e59d64e..c8dcedc79 100644 --- a/src/gausskernel/runtime/executor/execUtils.cpp +++ b/src/gausskernel/runtime/executor/execUtils.cpp @@ -1037,16 +1037,18 @@ Partition ExecOpenScanParitition(EState* estate, Relation parent, PartitionIdent * ExecInsertIndexTuples support * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecOpenIndices +/* + * ---------------------------------------------------------------- + * ExecOpenIndices * - * Find the indices associated with a result relation, open them, - * and save information about them in the result ResultRelInfo. + * 查找与结果关系关联的索引,打开它们, + * 并在结果 ResultRelInfo 中保存相关信息。 * - * At entry, caller has already opened and locked - * resultRelInfo->ri_RelationDesc. + * 在进入此函数时,调用者已经打开并锁定了 + * resultRelInfo->ri_RelationDesc。 * ---------------------------------------------------------------- */ + void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative) { Relation resultRelation = resultRelInfo->ri_RelationDesc; @@ -1059,35 +1061,27 @@ void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative) resultRelInfo->ri_NumIndices = 0; resultRelInfo->ri_ContainGPI = false; - /* fast path if no indexes */ + /* 如果没有索引,则使用快速路径 */ if (!RelationGetForm(resultRelation)->relhasindex) return; - /* - * Get cached list of index OIDs - */ + /* 获取缓存的索引 OID 列表 */ indexoidlist = RelationGetIndexList(resultRelation); len = list_length(indexoidlist); if (len == 0) { return; } - /* - * allocate space for result arrays - */ + /* 为结果数组分配空间 */ relationDescs = (RelationPtr)palloc(len * sizeof(Relation)); indexInfoArray = (IndexInfo**)palloc(len * sizeof(IndexInfo*)); resultRelInfo->ri_IndexRelationDescs = relationDescs; resultRelInfo->ri_IndexRelationInfo = indexInfoArray; - /* - * For each index, open the index relation and save pg_index info. We - * acquire RowExclusiveLock, signifying we will update the index. - * - * Note: we do this even if the index is not IndexIsReady; it's not worth - * the trouble to optimize for the case where it isn't. - */ + /* 对于每个索引,打开索引关系并保存pg_index信息。我们获取RowExclusiveLock,表示我们将更新索引。 + 注意:即使索引不是IndexIsReady,我们也会这样做;优化它不值得。 + */ i = 0; foreach (l, indexoidlist) { Oid indexOid = lfirst_oid(l); @@ -1096,24 +1090,24 @@ void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative) indexDesc = index_open(indexOid, RowExclusiveLock); - // ignore INSERT/UPDATE/DELETE on unusable index + // 忽略无法使用的索引上的INSERT/UPDATE/DELETE操作 if (!IndexIsUsable(indexDesc->rd_index)) { index_close(indexDesc, RowExclusiveLock); continue; } - /* Check index whether is global parition index, and save */ + // 检查索引是否为全局分区索引,然后保存 if (RelationIsGlobalIndex(indexDesc)) { resultRelInfo->ri_ContainGPI = true; } - /* extract index key information from the index's pg_index info */ + // 从索引的 pg_index 信息中提取索引键信息 ii = BuildIndexInfo(indexDesc); /* - * If the indexes are to be used for speculative insertion, add extra - * information required by unique index entries. - */ + * 如果索引将用于推测性插入,则需要添加唯一索引条目所需的额外信息。 + */ + if (speculative && ii->ii_Unique) { BuildSpeculativeIndexInfo(indexDesc, ii); } @@ -1121,7 +1115,8 @@ void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative) indexInfoArray[i] = ii; i++; } - // remember to set the number of usable indexes + +// 记得设置可用索引的数量 resultRelInfo->ri_NumIndices = i; list_free_ext(indexoidlist); @@ -1130,7 +1125,7 @@ void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative) /* ---------------------------------------------------------------- * ExecCloseIndices * - * Close the index relations stored in resultRelInfo + * 关闭存储在resultRelInfo中的索引关系 * ---------------------------------------------------------------- */ void ExecCloseIndices(ResultRelInfo* resultRelInfo) @@ -1146,19 +1141,16 @@ void ExecCloseIndices(ResultRelInfo* resultRelInfo) if (indexDescs[i] == NULL) continue; /* shouldn't happen? */ - /* Drop lock acquired by ExecOpenIndices */ + /* 释放ExecOpenIndices获取的锁 */ index_close(indexDescs[i], RowExclusiveLock); } - /* - * XXX should free indexInfo array here too? Currently we assume that - * such stuff will be cleaned up automatically in FreeExecutorState. - */ + /* XXX 应该在这里释放indexInfo数组吗?当前我们假设这些内容将在FreeExecutorState中自动清理。 */ + } -/* - * Copied from ExecInsertIndexTuples - */ +/* 从ExecInsertIndexTuples复制而来 */ + void ExecDeleteIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* estate, Relation targetPartRel, Partition p, const Bitmapset *modifiedIdxAttrs, const bool inplaceUpdated) { @@ -1190,19 +1182,19 @@ void ExecDeleteIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* es } /* - * Get information from the result relation info structure. + * 从结果关系信息结构中获取信息。 */ + relationDescs = resultRelInfo->ri_IndexRelationDescs; indexInfoArray = resultRelInfo->ri_IndexRelationInfo; heapRelation = resultRelInfo->ri_RelationDesc; - /* - * We will use the EState's per-tuple context for evaluating predicates - * and index 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; if (RELATION_IS_PARTITIONED(heapRelation)) { @@ -1225,9 +1217,7 @@ void ExecDeleteIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* es if (!RelationIsUstoreFormat(heapRelation)) return; - /* - * for each index, form and insert the index tuple - */ + /* 对于每个索引,生成并插入索引元组 */ for (int i = 0; i < numIndices; i++) { Relation indexRelation = relationDescs[i]; IndexInfo* indexInfo = NULL; @@ -1242,30 +1232,29 @@ void ExecDeleteIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* es indexInfo = indexInfoArray[i]; - /* If the index is marked as read-only, ignore it */ - /* XXXX: ???? */ + /* 如果索引标记为只读,忽略它 */ if (!indexInfo->ii_ReadyForInserts) { continue; } - /* modifiedIdxAttrs != NULL means updating, not every index are affected */ + /* modifiedIdxAttrs != NULL 表示更新操作,不是每个索引都受影响 */ if (inplaceUpdated && modifiedIdxAttrs != NULL) { - /* Collect attribute Bitmapset of this index, and compare with modifiedIdxAttrs */ + /* 收集此索引的属性 Bitmapset 并与 modifiedIdxAttrs 进行比较 */ Bitmapset *indexattrs = IndexGetAttrBitmap(indexRelation, indexInfo); bool overlap = bms_overlap(indexattrs, modifiedIdxAttrs); bms_free(indexattrs); if (!overlap) { - continue; /* related columns are not modified */ + continue; /* 相关列未被修改 */ } } - /* The GPI index insertion is the same as that of a common table */ + /* GPI索引插入与常规表相同 */ if (ispartitionedtable && !RelationIsGlobalIndex(indexRelation)) { partitionedindexid = RelationGetRelid(indexRelation); if (!PointerIsValid(partitionIndexOidList)) { partitionIndexOidList = PartitionGetPartIndexList(p); - // no local indexes available + // 没有可用的本地索引 if (!PointerIsValid(partitionIndexOidList)) { return; } @@ -1280,39 +1269,38 @@ void ExecDeleteIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* es actualindex, indexpartition, RowExclusiveLock); - // skip unusable index + // 跳过不可用的索引 if (indexpartition != NULL && indexpartition->pd_part != NULL && !indexpartition->pd_part->indisusable) { continue; } } else { actualindex = indexRelation; } - /* please adapt hash bucket for ustore here. Ref ExecInsertIndexTuples() */ + /* 请在这里适应 ustore 的哈希桶。参考 ExecInsertIndexTuples() 函数。 */ - /* Check for partial index */ + /* 检查部分索引 */ if (indexInfo->ii_Predicate != NIL) { List* predicate = NIL; - /* - * If predicate state not set up yet, create it (in the estate's - * per-query context) - */ + /* + * 如果断言状态尚未设置,则创建它(在estate的每个查询上下文中) + */ predicate = indexInfo->ii_PredicateState; if (predicate == NIL) { predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); indexInfo->ii_PredicateState = predicate; } - /* Skip this index-update if the predicate isn't satisfied */ + /* 如果断言未满足,则跳过此索引更新 */ if (!ExecQual(predicate, econtext, false)) { continue; } } - /* - * FormIndexDatum fills in its values and isnull parameters with the - * appropriate values for the column(s) of the index. + /* + * FormIndexDatum填充其values和isnull参数,以获得索引的列的适当值。 */ + FormIndexDatum(indexInfo, slot, estate, values, isnull); index_delete(actualindex, values, isnull, tupleid); @@ -1334,7 +1322,8 @@ void ExecUHeapDeleteIndexTuplesGuts( modifiedIdxAttrs, inplaceUpdated); } else { - UHeapTuple tmpUtup = ExecGetUHeapTupleFromSlot(oldslot); // materialize the tuple + UHeapTuple tmpUtup = ExecGetUHeapTupleFromSlot(oldslot);// 将元组材料化(将元组的内部格式转换为可以插入索引的格式) + tmpUtup->table_oid = RelationGetRelid(rel); ExecDeleteIndexTuples(oldslot, tupleid, @@ -1345,7 +1334,7 @@ void ExecUHeapDeleteIndexTuplesGuts( } } -/* purely for reducing cyclomatic complexity */ +/* 仅用于降低圈复杂性 */ static inline bool GetPartiionIndexOidList(List **oidlist_ptr, Partition part) { Assert(oidlist_ptr != NULL); @@ -1365,24 +1354,22 @@ static inline bool CheckForPartialIndex(IndexInfo* indexInfo, EState* estate, Ex List* predicate = indexInfo->ii_PredicateState; if (indexInfo->ii_Predicate != NIL) { - /* - * If predicate state not set up yet, create it (in the estate's - * per-query context) - */ + /* + * 如果谓词状态尚未设置,请在执行环境的每个查询上下文中创建它。 + */ + if (predicate == NIL) { predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); indexInfo->ii_PredicateState = predicate; } - /* Skip this index-update if the predicate isn't satisfied */ + /* 如果谓词不满足,则跳过这个索引更新 */ if (!ExecQual(predicate, econtext, false)) { return false; } } - /* - * If indexInfo->ii_Predicate == NIL, just return true to caller to proceed. - */ + /* 如果 indexInfo->ii_Predicate 为空,则直接返回 true,以便继续执行 */ return true; } @@ -1399,19 +1386,14 @@ static inline void SetInfoForUpsertGPI(bool isgpi, Relation *actualHeap, Relatio } } -/* ---------------------------------------------------------------- - * ExecCheckIndexConstraints +/* + * ExecCheckIndexConstraints * - * This routine checks if a tuple violates any unique or - * exclusion constraints. Returns true if there is no no conflict. - * Otherwise returns false, and the TID of the conflicting - * tuple is returned in *conflictTid. + * 此例程检查元组是否违反任何唯一或排除约束。如果没有冲突则返回true。 + * 否则返回false,并将冲突元组的TID存储在*conflictTid中。 * - * Note that this doesn't lock the values in any way, so it's - * possible that a conflicting tuple is inserted immediately - * after this returns. But this can be used for a pre-check - * before insertion. - * ---------------------------------------------------------------- + * 注意,这不会以任何方式锁定值,因此在此返回后,可能立即插入冲突的元组。 + * 但这可以用于插入之前的预检查。 */ bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate, Relation targetRel, Partition p, bool *isgpiResult, int2 bucketId, ConflictInfoData *conflictInfo, Oid *conflictPartOid, @@ -1438,9 +1420,9 @@ bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate, Relation ta ItemPointerSetInvalid(&conflictInfo->conflictTid); ItemPointerSetInvalid(&invalidItemPtr); - /* - * Get information from the result relation info structure. - */ + /* + * 从结果关系信息结构中获取信息。 + */ resultRelInfo = estate->es_result_relation_info; numIndices = resultRelInfo->ri_NumIndices; relationDescs = resultRelInfo->ri_IndexRelationDescs; @@ -1461,18 +1443,17 @@ bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate, Relation ta } } - /* - * use the EState's per-tuple context for evaluating predicates - * and index 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; - /* - * For each index, form index tuple and check if it satisfies the - * constraint. + /* + * 对于每个索引,形成索引元组并检查它是否满足约束。 */ for (i = 0; i < numIndices; i++) { Relation indexRelation = relationDescs[i]; @@ -1502,10 +1483,10 @@ bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate, Relation ta ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("INSERT ON DUPLICATE KEY UPDATE does not support deferrable" " unique constraints/exclusion constraints."))); - /* - * We consider a partitioned table with a global index as a normal table, - * because conflicts can be between multiple partitions. - */ + /* + * 我们将具有全局索引的分区表视为普通表,因为冲突可能发生在多个分区之间。 + */ + if (isPartitioned && !isgpi) { partitionedindexid = RelationGetRelid(indexRelation); @@ -1540,10 +1521,10 @@ bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate, Relation ta continue; } - /* - * FormIndexDatum fills in its values and isnull parameters with the - * appropriate values for the column(s) of the index. - */ + /* + * FormIndexDatum使用适当的值填充其值和isnull参数,以用于索引的列(s)。 + */ + FormIndexDatum(indexInfo, slot, estate, values, isnull); partoid = (isgpi ? p->pd_id : InvalidOid); @@ -1562,26 +1543,22 @@ bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate, Relation ta return true; } -/* ---------------------------------------------------------------- - * ExecInsertIndexTuples +/* + * 以注释的形式翻译: + + * ---------------------------------------------------------------- + * ExecInsertIndexTuples * - * This routine takes care of inserting index tuples - * into all the relations indexing the result relation - * when a heap tuple is inserted into the result relation. - * Much of this code should be moved into the genam - * stuff as it only exists here because the genam stuff - * doesn't provide the functionality needed by the - * executor.. -cim 9/27/89 + * 此例程负责在将堆元组插入结果关系时插入索引元组,所有索引关系都索引结果关系。 + * 大部分代码应该移到genam模块中,因为它只存在于此处是因为genam模块提供的功能不满足执行器所需。 + * -cim 1989年9月27日 * - * This returns a list of index OIDs for any unique or exclusion - * constraints that are deferred and that had - * potential (unconfirmed) conflicts. + * 此函数返回在唯一或排他约束中存在潜在(未确认)冲突且被推迟的情况下的所有索引OID列表。 * - * CAUTION: this must not be called for a HOT update. - * We can't defend against that here for lack of info. - * Should we change the API to make it safer? + * 注意:不能为HOT更新调用此函数。由于缺乏信息,我们无法在此处防范这种情况。我们是否应该更改API以使其更安全? * ---------------------------------------------------------------- */ + List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* estate, Relation targetPartRel, Partition p, int2 bucketId, bool* conflict, Bitmapset *modifiedIdxAttrs, bool inplaceUpdated) @@ -1602,8 +1579,9 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e List* partitionIndexOidList = NIL; /* - * Get information from the result relation info structure. + * 从结果关系信息结构中获取信息。 */ + resultRelInfo = estate->es_result_relation_info; numIndices = resultRelInfo->ri_NumIndices; relationDescs = resultRelInfo->ri_IndexRelationDescs; @@ -1612,12 +1590,13 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e containGPI = resultRelInfo->ri_ContainGPI; /* - * We will use the EState's per-tuple context for evaluating predicates - * and index 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; if (RELATION_IS_PARTITIONED(heapRelation)) { @@ -1630,7 +1609,7 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e if (p == NULL || p->pd_part == NULL) { return NIL; } - /* If the global partition index is included, the index insertion process needs to continue */ + /* 如果包括全局分区索引,则需要继续索引插入过程 */ if (!p->pd_part->indisusable && !containGPI) { numIndices = 0; } @@ -1642,17 +1621,16 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e searchHBucketFakeRelation(estate->esfRelations, estate->es_query_cxt, actualheap, bucketId, actualheap); } - /* Partition create in current transaction, set partition and rel reloption wait_clean_gpi */ + /* 在当前事务中创建分区,设置分区和关系的reloption为wait_clean_gpi */ if (RelationCreateInCurrXact(actualheap) && containGPI && !PartitionEnableWaitCleanGpi(p)) { - /* partition create not set wait_clean_gpi, must use update, and we ensure no concurrency */ + /* 如果分区创建时没有设置wait_clean_gpi,则必须使用更新,我们确保没有并发操作 */ PartitionSetWaitCleanGpi(RelationGetRelid(actualheap), true, false); - /* Partitioned create set wait_clean_gpi=n, and we want save it, so just use inplace */ + /* 分区创建设置wait_clean_gpi=n,我们想要保存它,所以只需使用inplace */ PartitionedSetWaitCleanGpi(RelationGetRelationName(heapRelation), RelationGetRelid(heapRelation), true, true); } - /* - * for each index, form and insert the index tuple - */ + /* 对于每个索引,形成并插入索引元组 */ + for (i = 0; i < numIndices; i++) { Relation indexRelation = relationDescs[i]; IndexInfo* indexInfo = NULL; @@ -1674,9 +1652,9 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e continue; } - /* modifiedIdxAttrs != NULL means updating, not every index are affected */ + /* modifiedIdxAttrs != NULL 意味着正在更新,不是每个索引都受影响 */ if (inplaceUpdated && modifiedIdxAttrs != NULL) { - /* Collect attribute Bitmapset of this index, and compare with modifiedIdxAttrs */ + /* 收集此索引的属性 Bitmapset,并与 modifiedIdxAttrs 进行比较 */ Bitmapset *indexattrs = IndexGetAttrBitmap(indexRelation, indexInfo); bool overlap = bms_overlap(indexattrs, modifiedIdxAttrs); @@ -1686,7 +1664,7 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e } } - /* The GPI index insertion is the same as that of a common table */ + /* 全局分区索引(GPI)的插入与普通表相同 */ if (ispartitionedtable && !RelationIsGlobalIndex(indexRelation)) { partitionedindexid = RelationGetRelid(indexRelation); if (!PointerIsValid(partitionIndexOidList)) { @@ -1721,38 +1699,34 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e if (indexInfo->ii_Predicate != NIL) { List* predicate = NIL; - /* - * If predicate state not set up yet, create it (in the estate's - * per-query context) - */ + /* + * 如果谓词状态尚未设置,就在estate的每个查询上下文中创建它。 + */ + predicate = indexInfo->ii_PredicateState; if (predicate == NIL) { predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); indexInfo->ii_PredicateState = predicate; } - /* Skip this index-update if the predicate isn't satisfied */ + /* 如果谓词不满足,则跳过这个索引更新 */ + if (!ExecQual(predicate, econtext, false)) { continue; } } - /* - * FormIndexDatum fills in its values and isnull parameters with the - * appropriate values for the column(s) of the index. - */ + /* + * FormIndexDatum会填充其values和isnull参数,其中包含索引的列的适当值。 + */ FormIndexDatum(indexInfo, slot, estate, values, isnull); - /* - * The index AM does the actual insertion, plus uniqueness checking. - * - * For an immediate-mode unique index, we just tell the index AM to - * throw error if not unique. - * - * For a deferrable unique index, we tell the index AM to just detect - * possible non-uniqueness, and we add the index OID to the result - * list if further checking is needed. - */ + /* + * 对于立即模式的唯一索引,我们只需告诉索引AM如果不唯一就抛出错误。 + * + * 对于可延迟的唯一索引,我们告诉索引AM仅检测可能的非唯一性,如果需要进一步检查,则将索引OID添加到结果列表中。 + */ + if (!indexRelation->rd_index->indisunique) { checkUnique = UNIQUE_CHECK_NO; } else if (conflict != NULL) { @@ -1770,17 +1744,14 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e actualheap, /* heap relation */ checkUnique); /* type of uniqueness check to do */ - /* - * If the index has an associated exclusion constraint, check that. - * This is simpler than the process for uniqueness checks since we - * always insert first and then check. If the constraint is deferred, - * we check now anyway, but don't throw error on violation; instead - * we'll queue a recheck event. - * - * An index for an exclusion constraint can't also be UNIQUE (not an - * essential property, we just don't allow it in the grammar), so no - * need to preserve the prior state of satisfiesConstraint. - */ + /* + * 如果索引有一个关联的排他约束,则进行检查。 + * 这比唯一性检查的过程简单,因为我们总是先插入然后再检查。 + * 如果约束被延迟,我们现在也进行检查,但不会在违反时抛出错误;相反,我们将排队重新检查事件。 + * + * 一个用于排他约束的索引也不能是唯一的(不是必需的属性,我们只是不允许在语法中使用它),所以不需要保留satisfiesConstraint的先前状态。 + */ + if (indexInfo->ii_ExclusionOps != NULL) { bool errorOK = !actualindex->rd_index->indimmediate; @@ -1789,12 +1760,11 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e } if ((IndexUniqueCheckNoError(checkUnique) || indexInfo->ii_ExclusionOps != NULL) && !satisfiesConstraint) { - /* - * The tuple potentially violates the uniqueness or exclusion - * constraint, so make a note of the index so that we can re-check - * it later. Speculative inserters are told if there was a - * speculative conflict, since that always requires a restart. - */ + /* + * 该元组可能违反唯一性或排除约束,因此请注意索引,以便稍后重新检查它。 + * 如果有投机性冲突,会告诉投机插入者,因为这总是需要重新开始。 + */ + result = lappend_oid(result, RelationGetRelid(indexRelation)); if (conflict != NULL) { *conflict = true; @@ -1807,29 +1777,23 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e } /* - * Check for violation of an exclusion constraint + * 检查排除约束是否违反 * - * heap: the table containing the new tuple - * index: the index supporting the exclusion constraint - * indexInfo: info about the index, including the exclusion properties - * tupleid: heap TID of the new tuple we have just inserted - * values, isnull: the *index* column values computed for the new tuple - * estate: an EState we can do evaluation in - * newIndex: if true, we are trying to build a new index (this affects - * only the wording of error messages) - * errorOK: if true, don't throw error for violation + * heap: 包含新元组的表 + * index: 支持排除约束的索引 + * indexInfo: 关于索引的信息,包括排除属性 + * tupleid: 我们刚刚插入的新元组的堆TID + * values, isnull: 为新元组计算的*索引*列值 + * estate: 我们可以在其中进行评估的EState + * newIndex: 如果为true,我们正在尝试构建新索引(这仅影响错误消息的措辞) + * errorOK: 如果为true,则不会因违规而抛出错误 * - * Returns true if OK, false if actual or potential violation + * 如果errorOK为true,我们会在不等待查看任何并发事务是否已提交的情况下报告违规;因此,违规仅是潜在的,调用者必须稍后重新检查。 + * 这种行为对于延迟的排除检查非常方便;如果在插入时明确没有冲突,我们就不必费心排队延迟事件。 * - * When errorOK is true, we report violation without waiting to see if any - * concurrent transaction has committed or not; so the violation is only - * potential, and the caller must recheck sometime later. This behavior - * is convenient for deferred exclusion checks; we need not bother queuing - * a deferred event if there is definitely no conflict at insertion time. - * - * When errorOK is false, we'll throw error on violation, so a false result - * is impossible. + * 当errorOK为false时,我们会在违规时抛出错误,因此不可能出现false的结果。 */ + bool check_exclusion_constraint(Relation heap, Relation index, IndexInfo* indexInfo, ItemPointer tupleid, Datum* values, const bool* isnull, EState* estate, bool newIndex, bool errorOK) { @@ -1842,12 +1806,12 @@ static inline IndexScanDesc scan_handler_idx_beginscan_wrapper(Relation parenthe { IndexScanDesc index_scan; if (RelationIsCrossBucketIndex(index) && RELATION_OWN_BUCKET(parentheap)) { - /* for cross-bucket index, pass parent relation to construct HBktIdxScanDesc */ + /* 对于跨桶索引,传递父关系以构造HBktIdxScanDesc */ index_scan = scan_handler_idx_beginscan(parentheap, index, snapshot, nkeys, norderbys, scan_state); HBktIdxScanDesc hpscan = (HBktIdxScanDesc)index_scan; - /* then set scan scope to target heap */ + /* 然后将扫描范围设置为目标堆 */ hpscan->currBktHeapRel = hpscan->currBktIdxScan->heapRelation = heap; - /* also make sure the target heap won't be released at the end of the scan */ + /* 同时确保目标堆在扫描结束时不会被释放 */ hpscan->rs_rd = heap; } else { index_scan = scan_handler_idx_beginscan(heap, index, snapshot, nkeys, norderbys, scan_state); @@ -1886,10 +1850,10 @@ bool check_violation(Relation heap, Relation index, IndexInfo *indexInfo, ItemPo TupleTableSlot* save_scantuple = NULL; Relation parentheap; - /* - * If any of the input values are NULL, the constraint check is assumed to - * pass (i.e., we assume the operators are strict). - */ + /* + * 如果任何输入值为NULL,则假定约束检查通过(即,我们假设操作符是严格的)。 + */ + for (i = 0; i < indnkeyatts; i++) { if (isnull[i]) { return true; @@ -1903,10 +1867,9 @@ bool check_violation(Relation heap, Relation index, IndexInfo *indexInfo, ItemPo constr_procs = indexInfo->ii_UniqueProcs; constr_strats = indexInfo->ii_UniqueStrats; } - /* - * Search the tuples that are in the index for any violations, including - * tuples that aren't visible yet. - */ + /* + * 在索引中搜索违规的元组,包括尚不可见的元组。 + */ InitDirtySnapshot(DirtySnapshot); for (i = 0; i < indnkeyatts; i++) { @@ -1914,27 +1877,27 @@ bool check_violation(Relation heap, Relation index, IndexInfo *indexInfo, ItemPo &scankeys[i], 0, i + 1, constr_strats[i], InvalidOid, index_collations[i], constr_procs[i], values[i]); } - /* - * Need a TupleTableSlot to put existing tuples in. - * - * To use FormIndexDatum, we have to make the econtext's scantuple point - * to this slot. Be sure to save and restore caller's value for - * scantuple. - */ + /* + * 需要一个 TupleTableSlot 用来放置现有的元组。 + * + * 为了使用 FormIndexDatum,我们必须让 econtext 的 scantuple 指向这个插槽。 + * 请确保保存并还原调用者对 scantuple 的值。 + */ + existing_slot = MakeSingleTupleTableSlot(RelationGetDescr(heap), false, heap->rd_tam_type); econtext = GetPerTupleExprContext(estate); save_scantuple = econtext->ecxt_scantuple; econtext->ecxt_scantuple = existing_slot; - /* - * May have to restart scan from this point if a potential conflict is - * found. - */ + /* + * 如果发现潜在的冲突,可能需要从此处重新开始扫描。 + */ + retry: conflict = false; found_self = false; - /* purely for reducing cyclomatic complexity */ + /* 仅仅是为了降低循环复杂度 */ parentheap = estate->es_result_relation_info->ri_RelationDesc; index_scan = scan_handler_idx_beginscan_wrapper(parentheap, heap, index, &DirtySnapshot, indnkeyatts, 0, NULL); scan_handler_idx_rescan_local(index_scan, scankeys, indnkeyatts, NULL, 0); @@ -1947,9 +1910,7 @@ retry: char* error_new = NULL; char* error_existing = NULL; - /* - * Ignore the entry for the tuple we're trying to check. - */ + /* 忽略我们要检查的元组的条目。 */ ItemPointer item = TUPLE_IS_UHEAP_TUPLE(tup) ? &((UHeapTuple)tup)->ctid : &((HeapTuple)tup)->t_self; if (ItemPointerIsValid(tupleid) && ItemPointerEquals(tupleid, item)) { if (found_self) /* should not happen */ @@ -1960,49 +1921,46 @@ retry: continue; } - /* - * Extract the index column values and isnull flags from the existing - * tuple. - */ + /* 从现有元组中提取索引列的值和isnull标志。 */ + (void)ExecStoreTuple(tup, existing_slot, InvalidBuffer, false); FormIndexDatum(indexInfo, existing_slot, estate, existing_values, existing_isnull); bool is_scan = index_scan_need_recheck(index_scan) && !index_recheck_constraint(index, constr_procs, existing_values, existing_isnull, values); - /* If lossy indexscan, must recheck the condition */ + /* 如果有信息损失的索引扫描,必须重新检查条件 */ if (is_scan) { - /* tuple doesn't actually match, so no conflict */ + /* 元组实际上不匹配,因此没有冲突 */ continue; } - /* - * At this point we have either a conflict or a potential conflict. - * If an in-progress transaction is affecting the visibility of this - * tuple, we need to wait for it to complete and then recheck (unless - * the caller requested not to). For simplicity we do rechecking by - * just restarting the whole scan --- this case probably doesn't - * happen often enough to be worth trying harder, and anyway we don't - * want to hold any index internal locks while waiting. - */ + /* + * 此时我们要么有一个冲突,要么有一个潜在冲突。 + * 如果一个正在进行的事务正在影响此元组的可见性,我们需要等待它完成然后重新检查(除非调用者要求不要这样做)。 + * 为了简化起见,我们通过重新启动整个扫描来进行重新检查 --- 这种情况可能不经常发生,不值得更加努力, + * 无论如何,我们都不想在等待期间持有任何索引内部锁。 + */ xwait = TransactionIdIsValid(DirtySnapshot.xmin) ? DirtySnapshot.xmin : DirtySnapshot.xmax; if (TransactionIdIsValid(xwait) && waitMode == CHECK_WAIT) { scan_handler_idx_endscan(index_scan); - /* for speculative insertion (INSERT ON DUPLICATE KEY UPDATE), - * we only need to wait the speculative token lock to be release, - * which happens when the tuple is speculative inserted by other - * running transction, and has done it's insertion (eithter - * finished or aborted). - */ + /* + * 对于投机插入(INSERT ON DUPLICATE KEY UPDATE), + * 我们只需要等待投机令牌锁被释放, + * 这发生在其他正在运行的事务通过投机插入元组并完成插入(要么完成了,要么中止了)时。 + */ + XactLockTableWait(xwait); goto retry; } - /* Determine whether the index column of the scanned tuple is the same - * as that of the tuple to be inserted. If not, the tuple pointed to by - * the item has been modified by other transactions. Check again for any conflicts. + /* + * 确定扫描的元组的索引列是否与要插入的元组相同。 + * 如果不同,表示该项目指向的元组已被其他事务修改。 + * 重新检查是否存在冲突。 */ + for (int i=0; i < indnkeyatts; i++) { if (existing_isnull[i] != isnull[i]) { conflict = false; @@ -2018,11 +1976,10 @@ retry: } } - /* - * We have a definite conflict (or a potential one, but the caller - * didn't want to wait). If we're not supposed to raise error, just - * return to the caller. - */ + /* + * 我们有一个明确的冲突(或潜在的冲突,但调用者不想等待)。 + * 如果我们不应该引发错误,只需返回给调用者。 + */ if (errorOK) { conflict = true; if (conflictInfo != NULL) { @@ -2034,10 +1991,10 @@ retry: break; } - /* - * We have a definite conflict (or a potential one, but the caller - * didn't want to wait). Report it. - */ + /* + * 我们有一个明确的冲突(或潜在的冲突,但调用者不想等待)。 + * 如果我们不应该引发错误,只需返回给调用者。 + */ error_new = BuildIndexValueDescription(index, values, isnull); error_existing = BuildIndexValueDescription(index, existing_values, existing_isnull); newIndex ? @@ -2058,13 +2015,13 @@ retry: scan_handler_idx_endscan(index_scan); - /* - * Ordinarily, at this point the search should have found the originally - * inserted tuple (if any), unless we exited the loop early because of conflict. - * However, it is possible to define exclusion constraints for which that - * wouldn't be true --- for instance, if the operator is <>. So we no - * longer complain if found_self is still false. - */ + /* + * 通常情况下,到了这一点,搜索应该已经找到了最初插入的元组(如果有的话), + * 除非我们因为冲突而提前退出了循环。然而,也有可能为排除约束定义这样的情况, + * 其中这个条件不成立 --- 例如,如果操作符是<>。 + * 因此,如果found_self仍然为false,我们将不再抱怨。 + */ + econtext->ecxt_scantuple = save_scantuple; ExecDropSingleTupleTableSlot(existing_slot); @@ -2073,9 +2030,10 @@ retry: } /* - * Check existing tuple's index values to see if it really matches the - * exclusion condition against the new_values. Returns true if conflict. + * 检查现有元组的索引值,看它是否与 new_values 真正匹配排除条件。 + * 如果有冲突,返回true。 */ + static bool index_recheck_constraint( Relation index, Oid* constr_procs, Datum* existing_values, const bool* existing_isnull, Datum* new_values) { @@ -2099,22 +2057,23 @@ static bool index_recheck_constraint( /* * UpdateChangedParamSet - * Add changed parameters to a plan node's chgParam set + * 将已更改的参数添加到计划节点的 chgParam 集合中 */ + void UpdateChangedParamSet(PlanState* node, Bitmapset* newchg) { Bitmapset* parmset = NULL; /* - * The plan node only depends on params listed in its allParam set. Don't - * include anything else into its chgParam set. - */ + * 计划节点仅依赖于其 allParam 集合中列出的参数。不要将其他任何东西包含在其 chgParam 集合中。 + */ + parmset = bms_intersect(node->plan->allParam, newchg); - /* - * Keep node->chgParam == NULL if there's not actually any members; this - * allows the simplest possible tests in executor node files. - */ + /* + * 如果实际上没有成员,则保持 node->chgParam == NULL;这允许在执行节点文件中进行最简单的测试。 + */ + if (!bms_is_empty(parmset)) node->chgParam = bms_join(node->chgParam, parmset); else @@ -2122,37 +2081,36 @@ void UpdateChangedParamSet(PlanState* node, Bitmapset* newchg) } /* - * Register a shutdown callback in an ExprContext. + * 在 ExprContext 中注册一个关闭回调。 * - * Shutdown callbacks will be called (in reverse order of registration) - * when the ExprContext is deleted or rescanned. This provides a hook - * for functions called in the context to do any cleanup needed --- it's - * particularly useful for functions returning sets. Note that the - * callback will *not* be called in the event that execution is aborted - * by an error. + * 关闭回调将在删除或重新扫描 ExprContext 时被调用(按注册的相反顺序)。 + * 这为在上下文中调用的函数提供了一个挂钩,用于进行所需的任何清理工作,尤其适用于返回集合的函数。 + * 请注意,如果由错误中止执行,则不会调用回调。 */ + void RegisterExprContextCallback(ExprContext* econtext, ExprContextCallbackFunction function, Datum arg) { ExprContext_CB* ecxt_callback = NULL; - /* Save the info in appropriate memory context */ + /* 将信息保存在适当的内存上下文中 */ ecxt_callback = (ExprContext_CB*)MemoryContextAlloc(econtext->ecxt_per_query_memory, sizeof(ExprContext_CB)); ecxt_callback->function = function; ecxt_callback->arg = arg; ecxt_callback->resowner = t_thrd.utils_cxt.CurrentResourceOwner; - /* link to front of list for appropriate execution order */ + /* 将信息保存在适当的内存上下文中 */ ecxt_callback->next = econtext->ecxt_callbacks; econtext->ecxt_callbacks = ecxt_callback; } /* - * Deregister a shutdown callback in an ExprContext. + * 在ExprContext中取消注册一个关闭回调函数。 * - * Any list entries matching the function and arg will be removed. - * This can be used if it's no longer necessary to call the callback. + * 任何匹配函数和参数的列表条目都将被删除。 + * 如果不再需要调用回调函数,则可以使用此函数。 */ + void UnregisterExprContextCallback(ExprContext* econtext, ExprContextCallbackFunction function, Datum arg) { ExprContext_CB** prev_callback = NULL; @@ -2170,14 +2128,14 @@ void UnregisterExprContextCallback(ExprContext* econtext, ExprContextCallbackFun } /* - * Call all the shutdown callbacks registered in an ExprContext. + * 调用在ExprContext中注册的所有关闭回调函数。 * - * The callback list is emptied (important in case this is only a rescan - * reset, and not deletion of the ExprContext). + * 回调函数列表将被清空(如果这仅是重新扫描重置,而不是删除ExprContext,则这很重要)。 * - * If isCommit is false, just clean the callback list but don't call 'em. - * (See comment for FreeExprContext.) + * 如果isCommit为false,则只清理回调列表但不调用回调函数。 + * (请参阅FreeExprContext的注释。) */ + static void ShutdownExprContext(ExprContext* econtext, bool isCommit) { ExprContext_CB* ecxt_callback = NULL; @@ -2187,15 +2145,16 @@ static void ShutdownExprContext(ExprContext* econtext, bool isCommit) if (econtext->ecxt_callbacks == NULL) return; - /* - * Call the callbacks in econtext's per-tuple context. This ensures that - * any memory they might leak will get cleaned up. - */ + /* + * 在econtext的每个元组上下文中调用回调函数。这可以确保它们可能泄漏的任何内存都将被清理。 + */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* - * Call each callback function in reverse registration order. - */ + * 按照注册顺序的相反顺序调用每个回调函数。 + */ + ResourceOwner oldOwner = t_thrd.utils_cxt.CurrentResourceOwner; PG_TRY(); { @@ -2219,6 +2178,22 @@ static void ShutdownExprContext(ExprContext* econtext, bool isCommit) MemoryContextSwitchTo(oldcontext); } +/* + * PthreadMutexLock - 尝试获取或等待一个pthread互斥锁 + * + * 此函数尝试获取一个pthread互斥锁。如果获取成功,函数返回0,否则返回错误码。 + * + * 参数: + * - owner: 资源拥有者,表示该互斥锁受此资源拥有者的管理。可以为NULL。 + * - mutex: 要获取的pthread互斥锁。 + * - trace: 是否启用跟踪标志,用于记录互斥锁的使用情况。 + * + * 注意: + * - 此函数在尝试获取互斥锁之前会禁用中断,以避免竞态条件。 + * - 如果指定了资源拥有者(owner非NULL),则函数将确保该资源拥有者已准备好用于存储pthread互斥锁的信息。 + * - 如果获取互斥锁成功且启用了跟踪标志,函数将记录该互斥锁的使用情况。 + * - 最后,函数会恢复中断状态,并返回获取互斥锁的结果(0表示成功,否则表示失败)。 + */ int PthreadMutexLock(ResourceOwner owner, pthread_mutex_t* mutex, bool trace) { @@ -2233,6 +2208,22 @@ int PthreadMutexLock(ResourceOwner owner, pthread_mutex_t* mutex, bool trace) RESUME_INTERRUPTS(); return ret; } +/* + * PthreadMutexTryLock - 尝试非阻塞获取pthread互斥锁 + * + * 此函数尝试非阻塞地获取一个pthread互斥锁。如果获取成功,函数返回0,否则返回错误码。 + * + * 参数: + * - owner: 资源拥有者,表示该互斥锁受此资源拥有者的管理。可以为NULL。 + * - mutex: 要获取的pthread互斥锁。 + * - trace: 是否启用跟踪标志,用于记录互斥锁的使用情况。 + * + * 注意: + * - 此函数在尝试获取互斥锁之前会禁用中断,以避免竞态条件。 + * - 如果指定了资源拥有者(owner非NULL),则函数将确保该资源拥有者已准备好用于存储pthread互斥锁的信息。 + * - 如果非阻塞获取互斥锁成功且启用了跟踪标志,函数将记录该互斥锁的使用情况。 + * - 最后,函数会恢复中断状态,并返回获取互斥锁的结果(0表示成功,否则表示失败)。 + */ int PthreadMutexTryLock(ResourceOwner owner, pthread_mutex_t* mutex, bool trace) { @@ -2248,6 +2239,7 @@ int PthreadMutexTryLock(ResourceOwner owner, pthread_mutex_t* mutex, bool trace) return ret; } +//释放一个 pthread 互斥锁(mutex) int PthreadMutexUnlock(ResourceOwner owner, pthread_mutex_t* mutex, bool trace) { HOLD_INTERRUPTS(); @@ -2258,7 +2250,7 @@ int PthreadMutexUnlock(ResourceOwner owner, pthread_mutex_t* mutex, bool trace) return ret; } - +//用于尝试以读取锁(read lock)的方式获取一个 pthread 读写锁(rwlock) int PthreadRWlockTryRdlock(ResourceOwner owner, pthread_rwlock_t* rwlock) { if (owner) { @@ -2277,7 +2269,7 @@ int PthreadRWlockTryRdlock(ResourceOwner owner, pthread_rwlock_t* rwlock) RESUME_INTERRUPTS(); return ret; } - +//用于以读取锁(read lock)的方式获取一个 pthread 读写锁(rwlock) void PthreadRWlockRdlock(ResourceOwner owner, pthread_rwlock_t* rwlock) { if (owner) { @@ -2298,6 +2290,7 @@ void PthreadRWlockRdlock(ResourceOwner owner, pthread_rwlock_t* rwlock) RESUME_INTERRUPTS(); } +//用于以尝试写入锁(try write lock)的方式获取一个 pthread 读写锁(rwlock) int PthreadRWlockTryWrlock(ResourceOwner owner, pthread_rwlock_t* rwlock) { if (owner) { @@ -2315,7 +2308,7 @@ int PthreadRWlockTryWrlock(ResourceOwner owner, pthread_rwlock_t* rwlock) RESUME_INTERRUPTS(); return ret; } - +//用于以阻塞方式获取一个 pthread 读写锁(rwlock)的写入锁(write lock) void PthreadRWlockWrlock(ResourceOwner owner, pthread_rwlock_t* rwlock) { if (owner) { @@ -2335,6 +2328,7 @@ void PthreadRWlockWrlock(ResourceOwner owner, pthread_rwlock_t* rwlock) } RESUME_INTERRUPTS(); } +//用于释放 pthread 读写锁(rwlock) void PthreadRWlockUnlock(ResourceOwner owner, pthread_rwlock_t* rwlock) { HOLD_INTERRUPTS(); @@ -2351,7 +2345,7 @@ void PthreadRWlockUnlock(ResourceOwner owner, pthread_rwlock_t* rwlock) } RESUME_INTERRUPTS(); } - +//用于初始化 pthread 读写锁(rwlock) void PthreadRwLockInit(pthread_rwlock_t* rwlock, pthread_rwlockattr_t *attr) { int ret = pthread_rwlock_init(rwlock, attr);