forked from huawei/openGauss-server
Update tcap_version.cpp
This commit is contained in:
parent
bcbebe4ccd
commit
e80e9b46c1
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue