From e80e9b46c19f6d1cc419829b494c8c7aed9e9704 Mon Sep 17 00:00:00 2001 From: nuoya <1204149038@qq.com> Date: Sun, 27 Aug 2023 16:13:15 +0800 Subject: [PATCH] Update tcap_version.cpp --- src/gausskernel/storage/tcap/tcap_version.cpp | 469 ++++++++++++------ 1 file changed, 316 insertions(+), 153 deletions(-) diff --git a/src/gausskernel/storage/tcap/tcap_version.cpp b/src/gausskernel/storage/tcap/tcap_version.cpp index aad39ed3f..20eb627bc 100644 --- a/src/gausskernel/storage/tcap/tcap_version.cpp +++ b/src/gausskernel/storage/tcap/tcap_version.cpp @@ -53,74 +53,78 @@ #include "storage/tcap.h" #include "catalog/pg_constraint.h" - +//用于判断指定关系表是否包含外键约束 static bool TvIsContainsForeignKey(Oid relid) { - Relation rbRel; - SysScanDesc sd; - ScanKeyData key; - HeapTuple tup; - bool isContainsForeignKey = false; + Relation rbRel; //用于访问系统目录表的关系对象 + SysScanDesc sd; //用于扫描系统目录表的扫描描述对象 + ScanKeyData key;//扫描键对象,用于设置扫描的条件 + HeapTuple tup; //表示系统目录表中的一个元组 + bool isContainsForeignKey = false;// 表示是否包含外键约束,初始值为假 + //打开系统目录表,并将结果赋值给变量 rbRel,以便后续的操作可以使用这个关系对象来访问系统目录表中的信息 rbRel = heap_open(ConstraintRelationId, AccessShareLock); - + ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(relid)); - + F_OIDEQ, ObjectIdGetDatum(relid));//初始化扫描键,用于在系统目录表上进行扫描 + //在系统目录表上启动一个扫描操作,并将扫描描述对象赋值给变量 sd,用于管理扫描过程 sd = systable_beginscan(rbRel, ConstraintRelidIndexId, true, SnapshotNow, 1, &key); while ((tup = systable_getnext(sd)) != NULL) { - Form_pg_constraint con = (Form_pg_constraint)GETSTRUCT(tup); + Form_pg_constraint con = (Form_pg_constraint)GETSTRUCT(tup);//获取元组的数据 /* Contains a foreign key or referenced by foreign key */ if (con->contype == CONSTRAINT_FOREIGN && con->conrelid == relid) { - isContainsForeignKey = true; + isContainsForeignKey = true;//如果找到符合条件的外键约束,设置标志为真 break; } } - systable_endscan(sd); + systable_endscan(sd);//结束扫描 heap_close(rbRel, AccessShareLock); - return isContainsForeignKey; + return isContainsForeignKey;//返回是否包含外键约束的结果 } - +// 用于检查指定关系表是否被外键约束引用 static bool TvIsReferencedByForeignKey(Oid relid) { - Relation rbRel; - SysScanDesc sd; - HeapTuple tup; - bool isReferencedByForeignKey = false; - - rbRel = heap_open(ConstraintRelationId, AccessShareLock); + Relation rbRel;//用于访问系统目录表的关系对象 + SysScanDesc sd;//用于扫描系统目录表的扫描描述对象 + HeapTuple tup; //表示系统目录表中的一个元组 + bool isReferencedByForeignKey = false;//表示是否被外键约束引用,初始值为假 + rbRel = heap_open(ConstraintRelationId, AccessShareLock);//打开系统目录表 + + //在系统目录表上启动一个扫描操作,无需索引,不锁定扫描行,使用快照为当前时刻,不设置扫描条件 sd = systable_beginscan(rbRel, InvalidOid, false, SnapshotNow, 0, NULL); while ((tup = systable_getnext(sd)) != NULL) { - Form_pg_constraint con = (Form_pg_constraint)GETSTRUCT(tup); + Form_pg_constraint con = (Form_pg_constraint)GETSTRUCT(tup);//获取元组的数据 /* Not referenced by foreign key */ if (con->confrelid == relid) { - isReferencedByForeignKey = true; + isReferencedByForeignKey = true;//如果找到符合条件的引用,设置标志为真 break; } } - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); + systable_endscan(sd);//结束扫描 + heap_close(rbRel, AccessShareLock);//关闭系统目录表 - return isReferencedByForeignKey; + return isReferencedByForeignKey;//返回是否被外键约束引用的结果 } - +//用于检查指定关系表是否包含外键约束或被外键约束引用 static bool TvForeignKeyCheck(Oid relid) -{ - return (TvIsContainsForeignKey(relid) || TvIsReferencedByForeignKey(relid)); +{ + //调用TvIsContainsForeignKey函数和TvIsReferencedByForeignKey函数来判断指定关系表是否包含外键约束或被外键约束引用 + //当返回true时说明此关系表包含外键约束或被外键约束引用,反之则不包含 + return (TvIsContainsForeignKey(relid) || TvIsReferencedByForeignKey(relid));/ } - +//此函数用于检查指定关系表是否支持TimeCapsule特性 static bool TvFeatureSupport(Oid relid, char **errstr, bool isTimecapsuleTable) { Relation rel = RelationIdGetRelation(relid); - Form_pg_class classForm; + Form_pg_class classForm;//存储表的元数据信息 if (!RelationIsValid(rel)) { ereport( @@ -128,9 +132,9 @@ static bool TvFeatureSupport(Oid relid, char **errstr, bool isTimecapsuleTable) errmsg("could not open relation with OID %u", relid))); } - classForm = rel->rd_rel; + classForm = rel->rd_rel;// 获取表的元数据信息 if (classForm->relkind != RELKIND_RELATION) { - *errstr = "timecapsule feature does not support non-ordinary table"; + *errstr = "timecapsule feature does not support non-ordinary table";//各种不支持TimeCapsule特性的原因 } else if (is_sys_table(RelationGetRelid(rel))) { *errstr = "timecapsule feature does not support system table"; } else if (classForm->relpersistence != RELPERSISTENCE_PERMANENT) { @@ -159,17 +163,19 @@ static bool TvFeatureSupport(Oid relid, char **errstr, bool isTimecapsuleTable) *errstr = NULL; } - RelationClose(rel); + RelationClose(rel);// 关闭表关系对象 return *errstr == NULL; } - +//用于在指定范围下检测表是否支持TimeCapsule特性 void TvCheckVersionScan(RangeTblEntry *rte) { - char *errstr = NULL; + char *errstr = NULL;// 初始化存储错误信息的字符串指针 + //调用TvFeatureSupport函数检查给定表是否支持 TimeCapsule 特性 if (!TvFeatureSupport(rte->relid, &errstr, false)) { + // 如果不支持,输出错误信息 ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errmsg("%s", errstr)))); @@ -178,69 +184,71 @@ void TvCheckVersionScan(RangeTblEntry *rte) return; } +//用于检查给定的扫描状态是否是版本表扫描 bool TvIsVersionScan(const ScanState *ss) { - EState *estate = ss->ps.state; - Scan *scan = (Scan *)ss->ps.plan; + EState *estate = ss->ps.state;//获取扫描状态关联的执行状态对象 + Scan *scan = (Scan *)ss->ps.plan;//获取扫描状态关联的扫描计划节点 TimeCapsuleClause *tcc = rt_fetch(scan->scanrelid, estate->es_range_table)->timecapsule; - - return tcc != NULL; + // 获取查询范围表中与扫描计划节点关联的 TimeCapsule 子句 + return tcc != NULL;//根据返回结果判断是否是版本表扫描 } /* * Whether the plan contains version table scan. */ +//用于检查给定的查询计划是否包含版本表扫描 bool TvIsVersionPlan(const PlannedStmt *stmt) { - ListCell *l = NULL; - foreach (l, stmt->rtable) { + ListCell *l = NULL;//初始化链表遍历用的指针 + foreach (l, stmt->rtable) {//遍历查询计划中的范围表条目 RangeTblEntry *rte = (RangeTblEntry *)lfirst(l); - if (rte->timecapsule != NULL) { - return true; + if (rte->timecapsule != NULL) {//如果范围表条目具有TimeCapsule子句 + return true;//返回真,说明包含版本表扫描 } } return false; } - +//用于将版本表达式规范化 Node *TvTransformVersionExpr(ParseState *pstate, TvVersionType tvtype, Node *tvver) { - Node *verExpr = tvver; + Node *verExpr = tvver;//用于存储版本表达式的变量 - verExpr = transformExpr(pstate, tvver); - if (checkExprHasSubLink(verExpr)) { + verExpr = transformExpr(pstate, tvver);//使用transformExpr函数转换版本表达式 + if (checkExprHasSubLink(verExpr)) {//检查表达式是否包含子链接 ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION), errmsg("timecapsule clause not support sublink."))); } - + //根据版本类型进行强制转换 if (tvtype == TV_VERSION_TIMESTAMP) { verExpr = coerce_to_specific_type(pstate, verExpr, TIMESTAMPTZOID, "TIMESTAMP"); } else { - verExpr = coerce_to_specific_type(pstate, verExpr, INT8OID, "CSN"); + verExpr = coerce_to_specific_type(pstate, verExpr, INT8OID, "CSN");//分配表达式的排序规则 } assign_expr_collations(pstate, verExpr); - return verExpr; + return verExpr;//返回规范化后的版本表达式 } - +//用于计算版本表达式的常量值 static Const *TvEvalVerExpr(TvVersionType tvtype, Node *tvver) { - Const *result = (Const *)tvver; + Const *result = (Const *)tvver;//用于存储版本表达式的常量结果 - if (!IsA(result, Const)) { + if (!IsA(result, Const)) {//如果版本表达式非常量 Node *verExpr; - ParseState *pstate = make_parsestate(NULL); - - verExpr = TvTransformVersionExpr(pstate, tvtype, tvver); - free_parsestate(pstate); + ParseState *pstate = make_parsestate(NULL);//创建解析状态对象 + verExpr = TvTransformVersionExpr(pstate, tvtype, tvver);//将版本表达式规范化 + free_parsestate(pstate);//释放解析状态对象 + // 使用evaluate_expr函数对转换后的表达式进行评估,获取常量值 result = (Const *)evaluate_expr((Expr *)verExpr, exprType(verExpr), exprTypmod(verExpr), exprCollation(verExpr)); } - + //检查结果是否是常量且非空 if (!IsA(result, Const) || result->constisnull) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -265,12 +273,12 @@ TransactionId TvFetchSnpxminRecycle(TimestampTz tz) Datum value; bool isnull = false; TransactionId snapxmin = FirstNormalTransactionId; - + // 打开快照表以进行访问 rel = heap_open(SnapshotRelationId, AccessShareLock); - + // 初始化扫描键,限制时间小于等于给定时间 ScanKeyInit(&skey[0], Anum_pg_snapshot_snptime, BTLessEqualStrategyNumber, F_TIMESTAMP_LE, TimestampTzGetDatum(tz)); - + // 开始扫描快照表,根据时间范围检索记录 sd = systable_beginscan(rel, SnapshotTimeCsnIndexId, true, NULL, 1, skey); tup = systable_getnext(sd); /* Limit 1 */ @@ -291,6 +299,7 @@ TransactionId TvFetchSnpxminRecycle(TimestampTz tz) * We use the round-down way to obtain snapshots. that is, * select * from gs_txn_snapshot where snptime <= :tz order by snptime desc limit 1; */ +// 获取指定时间的快照信息 static void TvFetchSnapTz(TimestampTz tz, Snapshot snap) { Relation rel; @@ -318,10 +327,10 @@ static void TvFetchSnapTz(TimestampTz tz, Snapshot snap) ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("cannot find the restore point"))); } - + //获取记录中的快照信息值 value = heap_getattr(tup, Anum_pg_snapshot_snpsnapshot, RelationGetDescr(rel), &isnull); snapstr = TextDatumGetCString(value); - + //反序列化快照信息字符串为快照对象 TxnSnapDeserialize(snapstr, snap); systable_endscan(sd); @@ -337,6 +346,7 @@ static void TvFetchSnapTz(TimestampTz tz, Snapshot snap) * We use the round-down way to obtain snapshots. that is, * select * from gs_txn_snapshot where snpcsn <= :csn order by snpcsn desc limit 1; */ +//跟CSN序列号获取快照信息 static void TvFetchSnapCsn(int64 csn, Snapshot snap) { Relation rel; @@ -380,6 +390,7 @@ static void TvFetchSnapCsn(int64 csn, Snapshot snap) * must be set to MaxTransactionId to ensure the correctness. */ if ((CommitSeqNo)csn != snap->snapshotcsn) { + //如果指定的CSN与快照的CSN不相等,则更新快照对象的一些属性 snap->snapshotcsn = (CommitSeqNo)csn; snap->timeline = 0; snap->xmin = snap->xmin; @@ -393,31 +404,38 @@ static void TvFetchSnapCsn(int64 csn, Snapshot snap) return; } - +//根据版本类型和值获取快照信息 static Snapshot TvFetchSnap(TvVersionType type, Const *value) { - Snapshot snap = (Snapshot)palloc0(sizeof(SnapshotData)); - + Snapshot snap = (Snapshot)palloc0(sizeof(SnapshotData));//为快照对象分配内存 + //根据版本类型获取快照信息 if (type == TV_VERSION_TIMESTAMP) { TvFetchSnapTz(DatumGetTimestampTz(value->constvalue), snap); } else { TvFetchSnapCsn(DatumGetInt64(value->constvalue), snap); } - snap->satisfies = SNAPSHOT_VERSION_MVCC; + snap->satisfies = SNAPSHOT_VERSION_MVCC;//将快照对象的隔离级别设置为多版本并发控制 return snap; } - +/* + * 功能:用于获取特定版本的快照对象 + * 参数: + * relation: 要获取快照的关系(表) + * tvtype: 版本类型(时间戳或 CSN) + * tvver: 用于确定快照的版本值 + */ static Snapshot TvGetSnap(Relation relation, TvVersionType tvtype, Node *tvver) { - Const *value; - Snapshot snap; + Const *value;//用于存储版本值的常量对象 + Snapshot snap;//快照对象 + //通过TvEvalVerExpr函数计算版本值 value = TvEvalVerExpr(tvtype, tvver); - + //验证快照是否适用于给定的关系 snap = TvFetchSnap(tvtype, value); - + //验证快照是否适用于给定的关系 if (!tableam_tcap_validate_snap(relation, snap)) { ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Restore point too old"))); @@ -425,18 +443,23 @@ static Snapshot TvGetSnap(Relation relation, TvVersionType tvtype, Node *tvver) return snap; } - +/* + * 功能:用于验证关系表的定义是否与指定的快照相匹配 + * 参数: + * - relid: 要验证的关系的 OID + * - snapcsn: 快照的提交序列号 + */ static void TvValidateRelDDL(Oid relid, CommitSeqNo snapcsn) { - Relation rel = RelationIdGetRelation(relid); - if (!RelationIsValid(rel)) { + Relation rel = RelationIdGetRelation(relid);//通过id获取要验证的关系 + if (!RelationIsValid(rel)) {//检查关系是否有效 ereport( - ERROR, (errcode(ERRCODE_RELATION_OPEN_ERROR), + ERROR, (errcode(ERRCODE_RELATION_OPEN_ERROR), //无效时报错 errmsg("could not open relation with OID %u", relid))); } - + //检查关系的变更CSN是否大于等于快照的CSN if (RelationGetChangecsn(rel) >= snapcsn) { - ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), //不匹配时报错 errmsg("The table definition of \"%s\" has been changed.", RelationGetRelationName(rel)))); } @@ -450,37 +473,51 @@ static void TvValidateRelDDL(Oid relid, CommitSeqNo snapcsn) * Choose user-specified snapshot if TimeCapsule clause exists, otherwise * estate->es_snapshot instead. */ +/* + * 功能:用于选择用于扫描的快照 + * 参数: + * relation: 扫描关联的关系对象 + * scan: 扫描计划节点 + * ss: 扫描状态 + */ Snapshot TvChooseScanSnap(Relation relation, Scan *scan, ScanState *ss) { - EState *estate = ss->ps.state; - Snapshot snap = estate->es_snapshot; + EState *estate = ss->ps.state;//获取执行状态对象 + Snapshot snap = estate->es_snapshot;//获取执行状态中的快照 + //获取范围表中与扫描关联的条目 RangeTblEntry *rte = rt_fetch(scan->scanrelid, estate->es_range_table); TimeCapsuleClause *tcc = rte->timecapsule; if (likely(tcc == NULL)) { - return snap; + return snap;//返回默认的快照 } else { bool isnull = false; ExprContext *econtext; Datum val; Const *con; - + //创建表达式的上下文信息 econtext = CreateExprContext(estate); val = ExecEvalExprSwitchContext(ExecInitExpr((Expr *)tcc->tvver, &ss->ps), - econtext, &isnull, NULL); + econtext, &isnull, NULL);//计算版本值 con = makeConst((tcc->tvtype == TV_VERSION_TIMESTAMP) ? TIMESTAMPTZOID : INT8OID, - -1, InvalidOid, 8, val, isnull, true); - + -1, InvalidOid, 8, val, isnull, true);//创建对应类型的常量节点 + //获取适用于指定版本的快照 snap = TvGetSnap(relation, tcc->tvtype, (Node *)con); + //验证快照版本是否允许执行相关操作 TvValidateRelDDL(rte->relid, snap->snapshotcsn); - + //释放表达式上下文和常量节点 FreeExprContext(econtext, true); pfree(con); } return snap; } - +/* + * 功能:用于根据快照删除表中的数据 + * 参数: + * relid: 关系对象的 OID + * snap: 用于删除数据的快照 + */ void TvDeleteDelta(Oid relid, Snapshot snap) { Relation rel; @@ -490,33 +527,42 @@ void TvDeleteDelta(Oid relid, Snapshot snap) /* Notice: invoker already acquired lock */ rel = heap_open(relid, NoLock); - sd = tableam_scan_begin(rel, snap, 0, NULL); + sd = tableam_scan_begin(rel, snap, 0, NULL);//开始扫描 while ((tup = (HeapTuple)tableam_scan_getnexttuple(sd, ForwardScanDirection)) != NULL) { - simple_heap_delete(rel, &tup->t_self); + simple_heap_delete(rel, &tup->t_self);//循环删除数据直至扫描结束 } tableam_scan_end(sd); heap_close(rel, NoLock); return; } - +/* + * 功能:用于根据给定的快照,删除关系或分区关系中不再需要的数据行 + * 参数: + * rel:主关系 + * partRel:分区关系 + * p:分区对象 + * snap:快照对象 + */ void TvUheapDeleteDeltaRel(Relation rel, Relation partRel, Partition p, Snapshot snap) { + //声明变量,用于存储扫描数据、事务标识等信息 TableScanDesc sd; UHeapTuple tup; TupleTableSlot *oldslot = NULL; TransactionId tmfdXmin = InvalidTransactionId; - - Snapshot snapshotNow = (Snapshot)palloc0(sizeof(SnapshotData)); + + Snapshot snapshotNow = (Snapshot)palloc0(sizeof(SnapshotData));//分配并初始化一个Snapshot对象 (void)GetSnapshotData(snapshotNow, false); - snap->user_data = (void *)snapshotNow; + snap->user_data = (void *)snapshotNow;//将快照与snapshotNow关联 - EState *estate = CreateExecutorState(); + EState *estate = CreateExecutorState();//创建执行状态 /* * We need a ResultRelInfo so we can use the regular executor's * index-entry-making machinery. (There used to be a huge amount of code * here that basically duplicated execUtils.c ...) */ + //创建一个ResultRelInfo对象 ResultRelInfo *resultRelInfo = makeNode(ResultRelInfo); resultRelInfo->ri_RangeTableIndex = 1; /* dummy */ resultRelInfo->ri_RelationDesc = rel; @@ -525,12 +571,14 @@ void TvUheapDeleteDeltaRel(Relation rel, Relation partRel, Partition p, Snapshot estate->es_num_result_relations = 1; estate->es_result_relation_info = resultRelInfo; + //根据是否存在分区关系选择要扫描的关系 Relation relRel = (partRel != NULL) ? partRel : rel; - sd = tableam_scan_begin(relRel, snap, 0, NULL); + sd = tableam_scan_begin(relRel, snap, 0, NULL);//根据选择的扫描关系初始化表扫描描述符 + while ((tup = (UHeapTuple)tableam_scan_getnexttuple(sd, ForwardScanDirection)) != NULL) { SimpleUHeapDelete(relRel, &tup->ctid, snapshotNow, &oldslot, &tmfdXmin); ExecDeleteIndexTuples(oldslot, &tup->ctid, estate, relRel, p, NULL, false); - if (relRel != NULL && relRel->rd_mlogoid != InvalidOid) { + if (relRel != NULL && relRel->rd_mlogoid != InvalidOid) {//如果关系存在并且具有有效的mlogoid,则将删除信息插入mlog表 insert_into_mlog_table(relRel, relRel->rd_mlogoid, NULL, &tup->ctid, tmfdXmin, 'D'); } if (oldslot) { @@ -554,11 +602,18 @@ void TvUheapDeleteDeltaRel(Relation rel, Relation partRel, Partition p, Snapshot return; } - +/* + * 功能:根据给定的快照,递归删除主关系及其所有分区关系中不再需要的数据行。 + * + * 参数列表: + * rel:主关系。 + * relid:主关系的OID。 + * snap:快照对象。 + */ void TvUheapDeleteDeltaPart(Relation rel, Oid relid, Snapshot snap) { - List* partTupleList = NIL; - ListCell* partCell = NULL; + List* partTupleList = NIL;//分区元组列表 + ListCell* partCell = NULL;//分区元组遍历指针 /* Open partition table, find all partition names based on the parentId. * partitioned table unspport the unlogged table. @@ -570,59 +625,91 @@ void TvUheapDeleteDeltaPart(Relation rel, Oid relid, Snapshot snap) foreach (partCell, partTupleList) { /* the "tup" just for get partOid, UHeapTup has no HEAP_HASOID flag, so here use HeapTuple */ HeapTuple tup = (HeapTuple)lfirst(partCell); - Oid partOid = HeapTupleGetOid(tup); - Partition p = partitionOpen(rel, partOid, AccessExclusiveLock); - Relation partRel = partitionGetRelation(rel, p); + Oid partOid = HeapTupleGetOid(tup);//获取分区OID + Partition p = partitionOpen(rel, partOid, AccessExclusiveLock);//打开分区 + Relation partRel = partitionGetRelation(rel, p);//获取分区关系 - if (RelationIsSubPartitioned(rel)) { - List* subPartTupleList = searchPgPartitionByParentId(PART_OBJ_TYPE_TABLE_SUB_PARTITION, partOid); - ListCell* subPartCell = NULL; - foreach (subPartCell, subPartTupleList) { + if (RelationIsSubPartitioned(rel)) {//当主关系存在子分区执行以下操作 + List* subPartTupleList = searchPgPartitionByParentId(PART_OBJ_TYPE_TABLE_SUB_PARTITION, partOid);//查找子分区元组列表 + ListCell* subPartCell = NULL;//初始化子分区元组遍历指针 + foreach (subPartCell, subPartTupleList) {//遍历子分区 HeapTuple subTup = (HeapTuple)lfirst(subPartCell); - Oid subPartOid = HeapTupleGetOid(subTup); - Partition subPar = partitionOpen(partRel, subPartOid, AccessExclusiveLock); - Relation subPartRel = partitionGetRelation(partRel, subPar); - TvUheapDeleteDeltaRel(rel, subPartRel, subPar, snap); - releaseDummyRelation(&subPartRel); - partitionClose(partRel, subPar, NoLock); + Oid subPartOid = HeapTupleGetOid(subTup);//获取子分区OID + Partition subPar = partitionOpen(partRel, subPartOid, AccessExclusiveLock);//子分区元组遍历 + Relation subPartRel = partitionGetRelation(partRel, subPar);//获取子分区关系 + TvUheapDeleteDeltaRel(rel, subPartRel, subPar, snap);//删除子分区的不再需要的数据行 + releaseDummyRelation(&subPartRel);//释放子分区关系 + partitionClose(partRel, subPar, NoLock);//关闭子分区 } - freePartList(subPartTupleList); + freePartList(subPartTupleList);//释放子分区元组列表 } else { - TvUheapDeleteDeltaRel(rel, partRel, p, snap); + TvUheapDeleteDeltaRel(rel, partRel, p, snap);//删除分区的不再需要的数据行 } - releaseDummyRelation(&partRel); - partitionClose(rel, p, NoLock); + releaseDummyRelation(&partRel);//释放分区关系 + partitionClose(rel, p, NoLock);//关闭分区 } - freePartList(partTupleList); + freePartList(partTupleList);//释放分区元组列表 return; } - +/* + * 功能:根据给定的快照,删除主关系或其分区关系中不再需要的数据行。 + * + * 参数列表: + * relid:关系的OID。 + * snap:快照对象。 + */ void TvUheapDeleteDelta(Oid relid, Snapshot snap) { - Relation rel = heap_open(relid, NoLock); - if (RELATION_IS_PARTITIONED(rel)) { + Relation rel = heap_open(relid, NoLock);//以NoLock无锁模式访问待操作的关系 + if (RELATION_IS_PARTITIONED(rel)) {//根据关系是否为分区表进行不同的操作 + //如果是分区表,则调用TvUheapDeleteDeltaPart函数来删除每个分区的Delta数据 TvUheapDeleteDeltaPart(rel, relid, snap); } else { + //否则直接调用TvUheapDeleteDeltaRel函数来删除Delta数据 TvUheapDeleteDeltaRel(rel, NULL, NULL, snap); } heap_close(rel, NoLock); } - +/* + * 功能:用于获取数据库中的元组 + * + * 参数列表: + * arg:函数指针 + */ typedef HeapTuple (*TvFetchTupleHook)(void *arg); static HeapTuple TvFetchTuple(void *arg) { + //调用tableam_scan_getnexttuple函数来获取下一个元组 HeapTuple tup = (HeapTuple)tableam_scan_getnexttuple((TableScanDesc)arg, ForwardScanDirection); - + //如果获取到了元组调用tableam_tops_copy_tuple函数对元组进行拷贝,并返回拷贝后的结果 + //否则,返回NULL表示没有获取到元组 return tup ? (HeapTuple)tableam_tops_copy_tuple(tup) : NULL; } - +/* + * 功能:用于获取数据库中的UHeapTuple元组 + * + * 参数列表: + * arg:函数指针 + */ typedef UHeapTuple (*TvUheapFetchTupleHook)(void *arg); static UHeapTuple TvUheapFetchTuple(void *arg) { return (UHeapTuple)tableam_scan_getnexttuple((TableScanDesc)arg, ForwardScanDirection); } - +/* + * 功能:用于向关系中批量插入元组,即批量插入操作 + * 参数: + * rel:要插入元组的关系。 + * estate:执行状态信息。 + * mycid:当前命令的 CommandId。 + * hiOptions:插入选项。 + * resultRelInfo:结果关系信息。 + * myslot:元组槽,用于临时存储元组。 + * bistate:批量插入状态信息。 + * nBufferedTuples:要插入的元组数量。 + * bufferedTuples:要插入的元组数组。 + */ static void TvBatchInsert(Relation rel, EState *estate, CommandId mycid, int hiOptions, ResultRelInfo *resultRelInfo, TupleTableSlot *myslot, BulkInsertState bistate, @@ -647,9 +734,9 @@ static void TvBatchInsert(Relation rel, EState *estate, CommandId mycid, int i; for (i = 0; i < nBufferedTuples; i++) { List *recheckIndexes = NULL; - + //将当前要插入的元组存储到元组槽中 (void)ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false); - + //执行索引插入操作,返回需要重新检查的索引列表 recheckIndexes = ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self), estate, @@ -658,7 +745,7 @@ static void TvBatchInsert(Relation rel, EState *estate, CommandId mycid, InvalidBktId, NULL, NULL); - + //释放重新检查索引列表的内存 list_free(recheckIndexes); } } @@ -668,6 +755,14 @@ static void TvBatchInsert(Relation rel, EState *estate, CommandId mycid, const int MAX_BUFFERED_TUPLES_TCAP = 1000; const int MAX_BUFFERED_TUPLES_NUM_TCAP = 65535; +/* + * 功能:用于执行插入丢失元组操作的实现函数。 + * 参数: + * rel:要插入元组的关系。 + * snap:当前事务的快照。 + * fetchTupleHook:获取元组的钩子函数。 + * arg:钩子函数的参数。 + */ static void TvInsertLostImpl(Relation rel, Snapshot snap, TvFetchTupleHook fetchTupleHook, void *arg) { HeapTuple tuple; @@ -680,8 +775,8 @@ static void TvInsertLostImpl(Relation rel, Snapshot snap, TvFetchTupleHook fetch BulkInsertState bistate; int nBufferedTuples = 0; - HeapTuple *bufferedTuples = NULL; - Size bufferedTuplesSize = 0; + HeapTuple *bufferedTuples = NULL;//缓冲的元组数组 + Size bufferedTuplesSize = 0;//缓冲的元组总大小 /* * We need a ResultRelInfo so we can use the regular executor's @@ -734,8 +829,8 @@ static void TvInsertLostImpl(Relation rel, Snapshot snap, TvFetchTupleHook fetch ExecConstraints(resultRelInfo, slot, estate); } - bufferedTuples[nBufferedTuples++] = tuple; - bufferedTuplesSize += tuple->t_len; + bufferedTuples[nBufferedTuples++] = tuple;//将元组加入缓冲数组 + bufferedTuplesSize += tuple->t_len;//增加缓冲元组总大小 /* * If the buffer filled up, flush it. Also flush if the total @@ -747,32 +842,43 @@ static void TvInsertLostImpl(Relation rel, Snapshot snap, TvFetchTupleHook fetch TvBatchInsert(rel, estate, mycid, hiOptions, resultRelInfo, myslot, bistate, nBufferedTuples, bufferedTuples); - nBufferedTuples = 0; - bufferedTuplesSize = 0; + nBufferedTuples = 0;//重置缓冲元组数量 + bufferedTuplesSize = 0;//重置缓冲元组数量 } } /* Flush any remaining buffered tuples */ if (nBufferedTuples > 0) { + //如果仍有缓冲的元组,调用TvBatchInsert函数将它们一并插入 TvBatchInsert(rel, estate, mycid, hiOptions, resultRelInfo, myslot, bistate, nBufferedTuples, bufferedTuples); } - + //释放BulkInsertState对象的资源 FreeBulkInsertState(bistate); - + //切换回旧的内存上下文 MemoryContextSwitchTo(oldcontext); - + //重置执行器的元组表,释放其中的资源 ExecResetTupleTable(estate->es_tupleTable, false); - + //关闭所有的索引 ExecCloseIndices(resultRelInfo); - + //释放执行状态对象的资源 FreeExecutorState(estate); - + //释放缓冲元组数组的内存 pfree(bufferedTuples); + //释放ResultRelInfo对象的内存 pfree(resultRelInfo); return; } - +/* + * 功能:在UHeap表中批量插入丢失的元组。 + * 参数列表: + * rel:表示要插入的关系 + * partRel:表示分区关系 + * p:分区对象 + * snap:快照对象 + * fetchTupleHook:元组获取钩子函数 + * arg:元组获取钩子函数的参数 + */ static void TvUheapInsertLostImpl(Relation rel, Relation partRel, Partition p, Snapshot snap, TvUheapFetchTupleHook fetchTupleHook, void *arg) { @@ -780,7 +886,7 @@ static void TvUheapInsertLostImpl(Relation rel, Relation partRel, Partition p, ResultRelInfo *resultRelInfo; EState *estate = CreateExecutorState(); TupleTableSlot *myslot; - CommandId mycid = GetCurrentCommandId(true); + CommandId mycid = GetCurrentCommandId(true);//获取当前命令的ID /* * We need a ResultRelInfo so we can use the regular executor's * index-entry-making machinery. (There used to be a huge amount of code @@ -789,12 +895,12 @@ static void TvUheapInsertLostImpl(Relation rel, Relation partRel, Partition p, resultRelInfo = makeNode(ResultRelInfo); resultRelInfo->ri_RangeTableIndex = 1; /* dummy */ resultRelInfo->ri_RelationDesc = rel; - ExecOpenIndices(resultRelInfo, false); + ExecOpenIndices(resultRelInfo, false);//打开索引 estate->es_result_relations = resultRelInfo; estate->es_num_result_relations = 1; estate->es_result_relation_info = resultRelInfo; - Relation relRel = (partRel != NULL) ? partRel : rel; + Relation relRel = (partRel != NULL) ? partRel : rel;//根据分区关系是否有指定选择要插入的表 /* Set up a tuple slot too */ myslot = ExecInitExtraTupleSlot(estate, TAM_USTORE); ExecSetSlotDescriptor(myslot, RelationGetDescr(relRel)); @@ -819,7 +925,9 @@ static void TvUheapInsertLostImpl(Relation rel, Relation partRel, Partition p, UHeapInsert(relRel, tuple, mycid, NULL); List *recheckIndexes = NULL; + //执行索引插入操作 recheckIndexes = ExecInsertIndexTuples(myslot, &tuple->ctid, estate, partRel, p, InvalidBktId, NULL, NULL); + //如果表有更改记录日志表(mlog),则插入相应的更改日志 if (relRel != NULL && relRel->rd_mlogoid != InvalidOid) { HeapTuple htup = NULL; Assert(relRel->rd_tam_type == TAM_USTORE); @@ -829,24 +937,29 @@ static void TvUheapInsertLostImpl(Relation rel, Relation partRel, Partition p, } list_free(recheckIndexes); } - MemoryContextSwitchTo(oldcontext); + MemoryContextSwitchTo(oldcontext);//切换回之前的内存上下文 - ExecResetTupleTable(estate->es_tupleTable, false); + ExecResetTupleTable(estate->es_tupleTable, false);//重置执行器的元组表 - ExecCloseIndices(resultRelInfo); + ExecCloseIndices(resultRelInfo);//关闭所有的索引 /* free the fakeRelationCache */ if (estate->esfRelations != NULL) { FakeRelationCacheDestroy(estate->esfRelations); } - FreeExecutorState(estate); + FreeExecutorState(estate);//释放执行状态对象的资源 pfree(resultRelInfo); return; } - +/* + * 功能:在给定的关系(表)中插入丢失的元组。 + * 参数列表: + * relid:要插入元组的关系(表)的标识符 + * snap:快照对象 + */ void TvInsertLost(Oid relid, Snapshot snap) { Relation rel; @@ -864,10 +977,18 @@ void TvInsertLost(Oid relid, Snapshot snap) heap_close(rel, NoLock); return; } - +/* + * 功能:用于在给定的关系(表)或其分区中插入丢失的元组。 + * 参数列表: + * rel:关系(表)的描述 + * partRel:分区关系(表)的描述 + * p:分区对象 + * snap:快照对象 + */ void TvUheapInsertLostRel(Relation rel, Relation partRel, Partition p, Snapshot snap) { TableScanDesc sd; + //如果partRel为NULL,则在关系(表)上开始扫描,否则在分区关系(表)上开始扫描 if (partRel == NULL) { sd = tableam_scan_begin(rel, snap, 0, NULL); } else { @@ -878,7 +999,13 @@ void TvUheapInsertLostRel(Relation rel, Relation partRel, Partition p, Snapshot tableam_scan_end(sd); return; } - +/* + * 功能:用于在给定的分区表的所有分区中插入丢失的元组。 + * 参数列表: + * rel:关系(表)的描述 + * relid:元组的关系(表)的标识符 + * snap:快照对象 + */ void TvUheapInsertLostPart(Relation rel, Oid relid, Snapshot snap) { List* partTupleList = NIL; @@ -898,20 +1025,29 @@ void TvUheapInsertLostPart(Relation rel, Oid relid, Snapshot snap) Partition p = partitionOpen(rel, partOid, AccessExclusiveLock); Relation partRel = partitionGetRelation(rel, p); - if (RelationIsSubPartitioned(rel)) { + if (RelationIsSubPartitioned(rel)) {//如果分区表存在子分区 + //获取所有子分区元组列表 List* subPartTupleList = searchPgPartitionByParentId(PART_OBJ_TYPE_TABLE_SUB_PARTITION, partOid); ListCell* subPartCell = NULL; + //遍历每个子分区元组 foreach (subPartCell, subPartTupleList) { + //获取子分区元组和其标识符 HeapTuple subTup = (HeapTuple)lfirst(subPartCell); Oid subPartOid = HeapTupleGetOid(subTup); + //打开子分区并获取子分区的描述 Partition subPar = partitionOpen(partRel, subPartOid, AccessExclusiveLock); Relation subPartRel = partitionGetRelation(partRel, subPar); + //调用TvUheapInsertLostRel函数插入丢失的元组到子分区中 TvUheapInsertLostRel(rel, subPartRel, subPar, snap); + //释放子分区的关系描述 releaseDummyRelation(&subPartRel); + //关闭子分区 partitionClose(partRel, subPar, AccessExclusiveLock); } + //释放子分区元组列表的内存 freePartList(subPartTupleList); } else { + //调用TvUheapInsertLostRel函数插入丢失的元组到当前分区中 TvUheapInsertLostRel(rel, partRel, p, snap); } releaseDummyRelation(&partRel); @@ -922,18 +1058,41 @@ void TvUheapInsertLostPart(Relation rel, Oid relid, Snapshot snap) } +/* + * 功能:用于将历史数据插入 TimeCapsule 版本表 + * + * 参数列表: + * relid:要执行还原的关系(表)OID + * snap:用于读取历史数据的快照 + */ void TvUheapInsertLost(Oid relid, Snapshot snap) { + // 打开给定 OID 对应的关系表,不加任何锁 Relation rel = heap_open(relid, NoLock); + + // 检查关系是否是分区表 if (RELATION_IS_PARTITIONED(rel)) { + // 如果是分区表,则执行 TvUheapInsertLostPart 函数,为每个分区执行历史数据插入操作 TvUheapInsertLostPart(rel, relid, snap); } else { + // 如果不是分区表,则执行 TvUheapInsertLostRel 函数,执行历史数据插入操作 + // 此处的 NULL 参数表示没有分区关系和分区,仅针对整个关系进行操作 TvUheapInsertLostRel(rel, NULL, NULL, snap); } + + // 关闭打开的关系表,不加任何锁 heap_close(rel, NoLock); + + // 函数结束,返回 return; } +/* + * 功能:检查是否允许还原特定版本,并执行还原操作 + * + * 参数列表: + * rel:要还原版本的关系(表)对象 + */ static void TvCheckVersionRestore(Relation rel) { char *errstr = NULL; @@ -956,7 +1115,12 @@ static void TvCheckVersionRestore(Relation rel) return; } - +/* + * 功能:执行版本还原操作 + * + * 参数列表: + * stmt:时间胶囊语句 + */ void TvRestoreVersion(TimeCapsuleStmt *stmt) { Relation rel; @@ -1001,4 +1165,3 @@ void TvRestoreVersion(TimeCapsuleStmt *stmt) return; } -