Update tcap_version.cpp

This commit is contained in:
nuoya 2023-08-27 16:13:15 +08:00
parent bcbebe4ccd
commit e80e9b46c1
1 changed files with 316 additions and 153 deletions

View File

@ -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
* relidOID
* 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;
}
/*
*
*
*
* relidOID
* 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
*
*
* relidOID
* 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;
}