Update inv_api.cpp
This commit is contained in:
parent
9124cc9411
commit
f6ddcc93b3
|
|
@ -62,121 +62,135 @@ static void open_lo_relation(void)
|
|||
if (t_thrd.storage_cxt.lo_heap_r && t_thrd.storage_cxt.lo_index_r)
|
||||
return; /* already open in current xact */
|
||||
|
||||
/* Arrange for the top xact to own these relation references */
|
||||
/* 获取当前的资源拥有者 */
|
||||
currentOwner = t_thrd.utils_cxt.CurrentResourceOwner;
|
||||
PG_TRY();
|
||||
{
|
||||
/* 将顶层事务的资源拥有者设置为当前资源拥有者 */
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = t_thrd.utils_cxt.TopTransactionResourceOwner;
|
||||
|
||||
/* Use RowExclusiveLock since we might either read or write */
|
||||
/* 使用RowExclusiveLock,因为我们可能需要读取或写入 */
|
||||
if (t_thrd.storage_cxt.lo_heap_r == NULL)
|
||||
/* 打开LargeObjectRelationId对应的heap关系,并使用RowExclusiveLock */
|
||||
t_thrd.storage_cxt.lo_heap_r = heap_open(LargeObjectRelationId, RowExclusiveLock);
|
||||
if (t_thrd.storage_cxt.lo_index_r == NULL)
|
||||
/* 打开LargeObjectLOidPNIndexId对应的index关系,并使用RowExclusiveLock */
|
||||
t_thrd.storage_cxt.lo_index_r = index_open(LargeObjectLOidPNIndexId, RowExclusiveLock);
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
/* Ensure CurrentResourceOwner is restored on error */
|
||||
/* 在错误发生时恢复CurrentResourceOwner的值 */
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = currentOwner;
|
||||
PG_RE_THROW();
|
||||
}
|
||||
PG_END_TRY();
|
||||
/* 恢复CurrentResourceOwner的值 */
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = currentOwner;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Clean up at main transaction end
|
||||
*/
|
||||
void close_lo_relation(bool isCommit)
|
||||
{
|
||||
if (t_thrd.storage_cxt.lo_heap_r || t_thrd.storage_cxt.lo_index_r) {
|
||||
if (t_thrd.storage_cxt.lo_heap_r || t_thrd.storage_cxt.lo_index_r) { // 如果lo_heap_r或lo_index_r不为空
|
||||
/*
|
||||
* Only bother to close if committing; else abort cleanup will handle
|
||||
* it
|
||||
* 只有在提交事务时才关闭,否则由于中止清理处理
|
||||
*/
|
||||
if (isCommit) {
|
||||
ResourceOwner currentOwner;
|
||||
if (isCommit) { // 如果是提交事务
|
||||
ResourceOwner currentOwner; // 当前资源拥有者
|
||||
|
||||
currentOwner = t_thrd.utils_cxt.CurrentResourceOwner;
|
||||
PG_TRY();
|
||||
currentOwner = t_thrd.utils_cxt.CurrentResourceOwner; // 保存当前资源拥有者
|
||||
PG_TRY(); // 尝试执行以下代码块
|
||||
{
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = t_thrd.utils_cxt.TopTransactionResourceOwner;
|
||||
t_thrd.utils_cxt.CurrentResourceOwner =
|
||||
t_thrd.utils_cxt.TopTransactionResourceOwner; // 设置当前资源拥有者为顶级事务资源拥有者
|
||||
|
||||
if (t_thrd.storage_cxt.lo_index_r)
|
||||
index_close(t_thrd.storage_cxt.lo_index_r, NoLock);
|
||||
if (t_thrd.storage_cxt.lo_heap_r)
|
||||
heap_close(t_thrd.storage_cxt.lo_heap_r, NoLock);
|
||||
if (t_thrd.storage_cxt.lo_index_r) // 如果lo_index_r不为空
|
||||
index_close(t_thrd.storage_cxt.lo_index_r, NoLock); // 关闭索引关系
|
||||
if (t_thrd.storage_cxt.lo_heap_r) // 如果lo_heap_r不为空
|
||||
heap_close(t_thrd.storage_cxt.lo_heap_r, NoLock); // 关闭堆关系
|
||||
}
|
||||
PG_CATCH();
|
||||
PG_CATCH(); // 捕获异常
|
||||
{
|
||||
/* Ensure CurrentResourceOwner is restored on error */
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = currentOwner;
|
||||
PG_RE_THROW();
|
||||
/* 确保错误时恢复CurrentResourceOwner */
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; // 恢复当前资源拥有者
|
||||
PG_RE_THROW(); // 重新抛出异常
|
||||
}
|
||||
PG_END_TRY();
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = currentOwner;
|
||||
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; // 恢复当前资源拥有者
|
||||
}
|
||||
t_thrd.storage_cxt.lo_heap_r = NULL;
|
||||
t_thrd.storage_cxt.lo_index_r = NULL;
|
||||
|
||||
t_thrd.storage_cxt.lo_heap_r = NULL; // 将lo_heap_r置为NULL
|
||||
t_thrd.storage_cxt.lo_index_r = NULL; // 将lo_index_r置为NULL
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Same as pg_largeobject.c's LargeObjectExists(), except snapshot to
|
||||
* read with can be specified.
|
||||
*/
|
||||
static bool myLargeObjectExists(Oid loid, Snapshot snapshot)
|
||||
{
|
||||
Relation pg_lo_meta;
|
||||
ScanKeyData skey[1];
|
||||
SysScanDesc sd;
|
||||
HeapTuple tuple;
|
||||
bool retval = false;
|
||||
Relation pg_lo_meta; // LargeObjectMetadataRelationId 对应的关系
|
||||
ScanKeyData skey[1]; // 扫描键
|
||||
SysScanDesc sd; // 系统扫描描述符
|
||||
HeapTuple tuple; // 堆元组
|
||||
bool retval = false; // 返回值,默认为false
|
||||
|
||||
// 初始化扫描键,设置ObjectIdAttributeNumber(对象ID属性编号)等于loid
|
||||
ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(loid));
|
||||
|
||||
// 打开LargeObjectMetadataRelationId对应的heap关系,并使用AccessShareLock进行锁定
|
||||
pg_lo_meta = heap_open(LargeObjectMetadataRelationId, AccessShareLock);
|
||||
|
||||
// 开始对pg_lo_meta关系进行系统扫描,使用LargeObjectMetadataOidIndexId对应的索引,
|
||||
// 忽略无效元组,指定快照为snapshot,扫描键数量为1,扫描键为skey
|
||||
sd = systable_beginscan(pg_lo_meta, LargeObjectMetadataOidIndexId, true, snapshot, 1, skey);
|
||||
|
||||
// 获取下一个堆元组
|
||||
tuple = systable_getnext(sd);
|
||||
if (HeapTupleIsValid(tuple))
|
||||
retval = true;
|
||||
retval = true; // 如果堆元组有效,则将retval设为true
|
||||
|
||||
// 结束系统扫描
|
||||
systable_endscan(sd);
|
||||
|
||||
// 关闭pg_lo_meta关系,释放AccessShareLock锁定的资源
|
||||
heap_close(pg_lo_meta, AccessShareLock);
|
||||
|
||||
return retval;
|
||||
return retval; // 返回retval值
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Extract data field from a pg_largeobject tuple, detoasting if needed
|
||||
* and verifying that the length is sane. Returns data pointer (a bytea *),
|
||||
* data length, and an indication of whether to pfree the data pointer.
|
||||
*/
|
||||
static void getdatafield(Form_pg_largeobject tuple, bytea** pdatafield, int* plen, bool* pfreeit)
|
||||
static void getdatafield(Form_pg_largeobject tuple, bytea **pdatafield, int *plen, bool *pfreeit)
|
||||
{
|
||||
bytea* datafield = NULL;
|
||||
int len;
|
||||
bool freeit = false;
|
||||
bytea *datafield = NULL; // 数据域指针
|
||||
int len; // 数据域长度
|
||||
bool freeit = false; // 是否需要释放数据域
|
||||
|
||||
datafield = &(tuple->data); /* see note at top of file */
|
||||
if (VARATT_IS_EXTENDED(datafield)) {
|
||||
datafield = (bytea*)heap_tuple_untoast_attr((struct varlena*)datafield);
|
||||
freeit = true;
|
||||
datafield = &(tuple->data); /* 见文件顶部的注释 */
|
||||
if (VARATT_IS_EXTENDED(datafield)) { // 如果数据域使用扩展存储
|
||||
datafield =
|
||||
(bytea *)heap_tuple_untoast_attr((struct varlena *)datafield); // 将扩展存储的数据域解压成栈上数据域
|
||||
freeit = true; // 设置需要释放数据域
|
||||
}
|
||||
len = VARSIZE(datafield) - VARHDRSZ;
|
||||
if (len < 0 || len > LOBLKSIZE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATA_CORRUPTED),
|
||||
errmsg("pg_largeobject entry for OID %u, page %d has invalid data field size %d",
|
||||
tuple->loid,
|
||||
tuple->pageno,
|
||||
len)));
|
||||
*pdatafield = datafield;
|
||||
*plen = len;
|
||||
*pfreeit = freeit;
|
||||
len = VARSIZE(datafield) - VARHDRSZ; // 获取数据域长度
|
||||
if (len < 0 || len > LOBLKSIZE) // 如果数据域长度不正确
|
||||
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED),
|
||||
errmsg("pg_largeobject entry for OID %u, page %d has invalid data field size %d", tuple->loid,
|
||||
tuple->pageno, len)));
|
||||
*pdatafield = datafield; // 返回数据域指针
|
||||
*plen = len; // 返回数据域长度
|
||||
*pfreeit = freeit; // 返回是否需要释放数据域
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -197,6 +211,7 @@ Oid inv_create(Oid lobjId)
|
|||
|
||||
/*
|
||||
* Create a new largeobject with empty data pages
|
||||
创建一个新的largeobject型对象,并设置为空数据页。
|
||||
*/
|
||||
lobjId_new = LargeObjectCreate(lobjId);
|
||||
|
||||
|
|
@ -230,51 +245,62 @@ Oid inv_create(Oid lobjId)
|
|||
* and subsidiary data are allocated in the specified memory context,
|
||||
* which must be suitably long-lived for the caller's purposes.
|
||||
*/
|
||||
LargeObjectDesc* inv_open(Oid lobjId, int flags, MemoryContext mcxt)
|
||||
LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt)
|
||||
{
|
||||
LargeObjectDesc* retval = NULL;
|
||||
LargeObjectDesc *retval = NULL; // 返回的LargeObjectDesc型对象描述符指针
|
||||
|
||||
retval = (LargeObjectDesc*)MemoryContextAlloc(mcxt, sizeof(LargeObjectDesc));
|
||||
retval =
|
||||
(LargeObjectDesc *)MemoryContextAlloc(mcxt, sizeof(LargeObjectDesc)); // 分配内存空间用于存储LargeObjectDesc型对象描述符
|
||||
|
||||
retval->id = lobjId;
|
||||
retval->subid = GetCurrentSubTransactionId();
|
||||
retval->offset = 0;
|
||||
retval->id = lobjId; // 设置LargeObjectDesc型对象的OID
|
||||
retval->subid = GetCurrentSubTransactionId(); // 设置当前子事务ID
|
||||
retval->offset = 0; // 设置偏移量
|
||||
|
||||
if (flags & INV_WRITE) {
|
||||
retval->snapshot = SnapshotNow;
|
||||
retval->flags = IFS_WRLOCK | IFS_RDLOCK;
|
||||
} else if (flags & INV_READ) {
|
||||
if (flags & INV_WRITE) { // 如果标志包含INV_WRITE,表示以写模式打开LargeObjectDesc型对象
|
||||
retval->snapshot = SnapshotNow; // 使用当前快照
|
||||
retval->flags = IFS_WRLOCK | IFS_RDLOCK; // 设置写锁和读锁标志
|
||||
} else if (flags & INV_READ) { // 如果标志包含INV_READ,表示以读模式打开LargeObjectDesc型对象
|
||||
/*
|
||||
* We must register the snapshot in TopTransaction's resowner, because
|
||||
* it must stay alive until the LO is closed rather than until the
|
||||
* current portal shuts down.
|
||||
* 必须在TopTransaction的资源拥有者中注册快照,
|
||||
* 因为它必须在LO关闭之前保持活动状态,而不是在当前portal关闭时。
|
||||
*/
|
||||
retval->snapshot = RegisterSnapshotOnOwner(GetActiveSnapshot(), t_thrd.utils_cxt.TopTransactionResourceOwner);
|
||||
retval->flags = IFS_RDLOCK;
|
||||
} else
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid flags: %d", flags)));
|
||||
retval->snapshot = RegisterSnapshotOnOwner(
|
||||
GetActiveSnapshot(), t_thrd.utils_cxt.TopTransactionResourceOwner); // 在资源拥有者中注册快照
|
||||
retval->flags = IFS_RDLOCK; // 设置读锁标志
|
||||
} else {
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid flags: %d", flags))); // 抛出错误,标志无效
|
||||
}
|
||||
|
||||
/* Can't use LargeObjectExists here because it always uses SnapshotNow */
|
||||
if (!myLargeObjectExists(lobjId, retval->snapshot))
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("large object %u does not exist", lobjId)));
|
||||
// 不能使用LargeObjectExists,因为它总是使用SnapshotNow
|
||||
if (!myLargeObjectExists(lobjId, retval->snapshot)) { // 如果大型对象不存在
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("large object %u does not exist", lobjId))); // 抛出错误,大型对象不存在
|
||||
}
|
||||
|
||||
return retval;
|
||||
return retval; // 返回LargeObjectDesc型对象描述符指针
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes a large object descriptor previously made by inv_open(), and
|
||||
* releases the long-term memory used by it.
|
||||
*/
|
||||
void inv_close(LargeObjectDesc* obj_desc)
|
||||
void inv_close(LargeObjectDesc *obj_desc)
|
||||
{
|
||||
Assert(PointerIsValid(obj_desc));
|
||||
|
||||
/*
|
||||
* 检查obj_desc是否有效
|
||||
*/
|
||||
if (obj_desc->snapshot != SnapshotNow)
|
||||
UnregisterSnapshotFromOwner(obj_desc->snapshot, t_thrd.utils_cxt.TopTransactionResourceOwner);
|
||||
|
||||
/*
|
||||
* 释放obj_desc的内存空间
|
||||
*/
|
||||
pfree(obj_desc);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Destroys an existing large object (not to be confused with a descriptor!)
|
||||
*
|
||||
|
|
@ -287,18 +313,19 @@ int inv_drop(Oid lobjId)
|
|||
/*
|
||||
* Delete any comments and dependencies on the large object
|
||||
*/
|
||||
object.classId = LargeObjectRelationId;
|
||||
object.objectId = lobjId;
|
||||
object.objectSubId = 0;
|
||||
performDeletion(&object, DROP_CASCADE, 0);
|
||||
object.classId = LargeObjectRelationId; // 设置对象类别为大型对象
|
||||
object.objectId = lobjId; // 设置对象ID为目标大型对象的OID
|
||||
object.objectSubId = 0; // 设置对象子ID为0
|
||||
performDeletion(&object, DROP_CASCADE, 0); // 执行删除操作,级联删除依赖项
|
||||
|
||||
/*
|
||||
* Advance command counter so that tuple removal will be seen by later
|
||||
* large-object operations in this transaction.
|
||||
* 提升命令计数器以便后续事务中的元组移除可以被看到
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
CommandCounterIncrement();// 提升命令计数器
|
||||
|
||||
return 1;
|
||||
return 1;// 返回1表示删除成功
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -314,14 +341,24 @@ static uint32 inv_getsize(LargeObjectDesc* obj_desc)
|
|||
SysScanDesc sd;
|
||||
HeapTuple tuple;
|
||||
|
||||
/*
|
||||
* 检查obj_desc是否有效
|
||||
*/
|
||||
Assert(PointerIsValid(obj_desc));
|
||||
|
||||
/* 打开pg_largeobject关系 */
|
||||
open_lo_relation();
|
||||
|
||||
/*
|
||||
* 初始化扫描键信息
|
||||
*/
|
||||
ScanKeyInit(&skey[0], Anum_pg_largeobject_loid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(obj_desc->id));
|
||||
|
||||
sd = systable_beginscan_ordered(
|
||||
t_thrd.storage_cxt.lo_heap_r, t_thrd.storage_cxt.lo_index_r, obj_desc->snapshot, 1, skey);
|
||||
/*
|
||||
* 有序扫描pg_largeobject索引,查找对应的元组。
|
||||
*/
|
||||
sd = systable_beginscan_ordered(t_thrd.storage_cxt.lo_heap_r, t_thrd.storage_cxt.lo_index_r, obj_desc->snapshot, 1,
|
||||
skey);
|
||||
|
||||
/*
|
||||
* Because the pg_largeobject index is on both loid and pageno, but we
|
||||
|
|
@ -336,136 +373,152 @@ static uint32 inv_getsize(LargeObjectDesc* obj_desc)
|
|||
int len;
|
||||
bool pfreeit = false;
|
||||
|
||||
/* 如果存在null值,则错误 */
|
||||
if (HeapTupleHasNulls(tuple)) /* paranoia */
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("null field found in pg_largeobject")));
|
||||
/* 获取元组中的数据 */
|
||||
data = (Form_pg_largeobject)GETSTRUCT(tuple);
|
||||
getdatafield(data, &datafield, &len, &pfreeit);
|
||||
|
||||
/* 计算最后一个字节的位置 */
|
||||
lastbyte = data->pageno * LOBLKSIZE + len;
|
||||
|
||||
/* 释放datafield指向的内存空间 */
|
||||
if (pfreeit)
|
||||
pfree(datafield);
|
||||
}
|
||||
|
||||
/*
|
||||
* 终止扫描
|
||||
*/
|
||||
systable_endscan_ordered(sd);
|
||||
|
||||
return lastbyte;
|
||||
}
|
||||
|
||||
int inv_seek(LargeObjectDesc* obj_desc, int offset, int whence)
|
||||
int inv_seek(LargeObjectDesc *obj_desc, int offset, int whence)
|
||||
{
|
||||
Assert(PointerIsValid(obj_desc));
|
||||
Assert(PointerIsValid(obj_desc)); // 断言大型对象描述符指针有效
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
if (offset < 0)
|
||||
case SEEK_SET: // 从文件开头开始计算offset
|
||||
if (offset < 0) // 如果offset为负数,则抛出错误
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("invalid seek offset: %d", offset)));
|
||||
obj_desc->offset = offset;
|
||||
obj_desc->offset = offset; // 设置偏移量为offset
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
if (offset < 0 && obj_desc->offset < ((uint32)(-offset)))
|
||||
case SEEK_CUR: // 相对于当前位置计算offset
|
||||
if (offset < 0 &&
|
||||
obj_desc->offset < ((uint32)(-offset))) // 如果offset为负数且偏移量小于(-offset),则抛出错误
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("invalid seek offset: %d", offset)));
|
||||
obj_desc->offset += offset;
|
||||
obj_desc->offset += offset; // 偏移量增加offset
|
||||
break;
|
||||
case SEEK_END: {
|
||||
uint32 size = inv_getsize(obj_desc);
|
||||
if (offset < 0 && size < ((uint32)(-offset)))
|
||||
case SEEK_END: { // 相对于文件末尾计算offset
|
||||
uint32 size = inv_getsize(obj_desc); // 获取大型对象的大小
|
||||
if (offset < 0 && size < ((uint32)(-offset))) // 如果offset为负数且对象大小小于(-offset),则抛出错误
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("invalid seek offset: %d", offset)));
|
||||
obj_desc->offset = size + offset;
|
||||
obj_desc->offset = size + offset; // 偏移量设置为对象大小加offset
|
||||
} break;
|
||||
default:
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("invalid whence: %d", whence)));
|
||||
ereport(ERROR, (errcode_for_file_access(),
|
||||
errmsg("invalid whence: %d", whence))); // 如果whence参数无效,则抛出错误
|
||||
}
|
||||
return obj_desc->offset; // 返回当前偏移量
|
||||
}
|
||||
|
||||
int inv_tell(LargeObjectDesc *obj_desc)
|
||||
{
|
||||
// 检查obj_desc是否有效
|
||||
Assert(PointerIsValid(obj_desc));
|
||||
|
||||
// 返回obj_desc中的offset字段值
|
||||
return obj_desc->offset;
|
||||
}
|
||||
|
||||
int inv_tell(LargeObjectDesc* obj_desc)
|
||||
{
|
||||
Assert(PointerIsValid(obj_desc));
|
||||
|
||||
return obj_desc->offset;
|
||||
}
|
||||
|
||||
int inv_read(LargeObjectDesc* obj_desc, char* buf, int nbytes)
|
||||
int inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
|
||||
{
|
||||
Assert(PointerIsValid(obj_desc));
|
||||
int nread = 0;
|
||||
Assert(PointerIsValid(obj_desc)); // 断言大型对象描述符指针有效
|
||||
|
||||
int nread = 0; // 已读取的字节数
|
||||
int n;
|
||||
int off;
|
||||
int len;
|
||||
int32 pageno = (int32)(obj_desc->offset / LOBLKSIZE);
|
||||
int32 pageno = (int32)(obj_desc->offset / LOBLKSIZE); // 根据偏移量计算页号
|
||||
uint32 pageoff;
|
||||
ScanKeyData skey[2];
|
||||
SysScanDesc sd;
|
||||
HeapTuple tuple;
|
||||
errno_t rc = EOK;
|
||||
|
||||
Assert(buf != NULL);
|
||||
Assert(buf != NULL); // 断言缓冲区指针有效
|
||||
|
||||
if (nbytes <= 0) {
|
||||
if (nbytes <= 0) { // 如果要读取的字节数小于等于0,则直接返回0
|
||||
return 0;
|
||||
}
|
||||
|
||||
open_lo_relation();
|
||||
open_lo_relation(); // 打开大型对象关系表
|
||||
|
||||
// 初始化扫描键值
|
||||
ScanKeyInit(&skey[0], Anum_pg_largeobject_loid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(obj_desc->id));
|
||||
|
||||
ScanKeyInit(&skey[1], Anum_pg_largeobject_pageno, BTGreaterEqualStrategyNumber, F_INT4GE, Int32GetDatum(pageno));
|
||||
|
||||
sd = systable_beginscan_ordered(
|
||||
t_thrd.storage_cxt.lo_heap_r, t_thrd.storage_cxt.lo_index_r, obj_desc->snapshot, 2, skey);
|
||||
// 开始有序扫描大型对象索引表
|
||||
sd = systable_beginscan_ordered(t_thrd.storage_cxt.lo_heap_r, t_thrd.storage_cxt.lo_index_r, obj_desc->snapshot, 2,
|
||||
skey);
|
||||
|
||||
while ((tuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL) {
|
||||
Form_pg_largeobject data;
|
||||
bytea* datafield = NULL;
|
||||
bytea *datafield = NULL;
|
||||
bool pfreeit = false;
|
||||
|
||||
if (HeapTupleHasNulls(tuple)) /* paranoia */
|
||||
if (HeapTupleHasNulls(tuple)) // 如果元组有空字段,抛出错误
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("null field found in pg_largeobject")));
|
||||
data = (Form_pg_largeobject)GETSTRUCT(tuple);
|
||||
data = (Form_pg_largeobject)GETSTRUCT(tuple); // 获取大型对象元组的数据
|
||||
|
||||
/*
|
||||
* We expect the indexscan will deliver pages in order. However,
|
||||
* there may be missing pages if the LO contains unwritten "holes". We
|
||||
* want missing sections to read out as zeroes.
|
||||
*/
|
||||
pageoff = ((uint32)data->pageno) * LOBLKSIZE;
|
||||
if (pageoff > obj_desc->offset) {
|
||||
pageoff = ((uint32)data->pageno) * LOBLKSIZE; // 计算页的偏移量
|
||||
if (pageoff > obj_desc->offset) { // 如果页的偏移量大于当前偏移量,则说明存在缺失的部分,将其读出为零
|
||||
n = pageoff - obj_desc->offset;
|
||||
n = (n <= (nbytes - nread)) ? n : (nbytes - nread);
|
||||
rc = memset_s(buf + nread, n, '\0', n);
|
||||
rc = memset_s(buf + nread, n, '\0', n); // 将buf中的一部分置为零
|
||||
securec_check(rc, "", "");
|
||||
nread += n;
|
||||
obj_desc->offset += n;
|
||||
}
|
||||
|
||||
if (nread < nbytes) {
|
||||
off = (int)(obj_desc->offset - pageoff);
|
||||
if (off < 0 || off >= LOBLKSIZE) {
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("invalid offset num:%d", off)));
|
||||
off = (int)(obj_desc->offset - pageoff); // 计算在当前页内的偏移量
|
||||
if (off < 0 || off >= LOBLKSIZE) { // 如果偏移量不合法,抛出错误
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid offset num:%d", off)));
|
||||
}
|
||||
|
||||
getdatafield(data, &datafield, &len, &pfreeit);
|
||||
if (len > off) {
|
||||
getdatafield(data, &datafield, &len, &pfreeit); // 获取大型对象数据字段
|
||||
if (len > off) { // 如果数据字段内有足够的数据可供读取
|
||||
n = len - off;
|
||||
n = (n <= (nbytes - nread)) ? n : (nbytes - nread);
|
||||
rc = memcpy_s(buf + nread, n, VARDATA(datafield) + off, n);
|
||||
rc = memcpy_s(buf + nread, n, VARDATA(datafield) + off, n); // 将数据复制到buf中
|
||||
securec_check(rc, "", "");
|
||||
nread += n;
|
||||
obj_desc->offset += n;
|
||||
}
|
||||
if (pfreeit)
|
||||
pfree(datafield);
|
||||
pfree(datafield); // 释放大型对象数据字段的内存
|
||||
}
|
||||
|
||||
if (nread >= nbytes)
|
||||
break;
|
||||
}
|
||||
|
||||
systable_endscan_ordered(sd);
|
||||
systable_endscan_ordered(sd); // 结束扫描
|
||||
|
||||
return nread;
|
||||
return nread; // 返回已读取的字节数
|
||||
}
|
||||
|
||||
|
||||
void check_obj_desc(const LargeObjectDesc* obj_desc)
|
||||
{
|
||||
/* enforce writability because snapshot is probably wrong otherwise */
|
||||
|
|
@ -480,56 +533,57 @@ void check_obj_desc(const LargeObjectDesc* obj_desc)
|
|||
ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("large object %u was already dropped", obj_desc->id)));
|
||||
}
|
||||
|
||||
int inv_write(LargeObjectDesc* obj_desc, const char* buf, int nbytes)
|
||||
int inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
|
||||
{
|
||||
if (nbytes <= 0) {
|
||||
return 0;
|
||||
}
|
||||
int nwritten = 0;
|
||||
int n;
|
||||
int off;
|
||||
int len;
|
||||
int32 pageno;
|
||||
ScanKeyData skey[2];
|
||||
SysScanDesc sd;
|
||||
HeapTuple oldtuple = NULL;
|
||||
Form_pg_largeobject olddata = NULL;
|
||||
bool neednextpage = true;
|
||||
bytea* datafield = NULL;
|
||||
bool pfreeit = false;
|
||||
int nwritten = 0; // 已写入的字节数
|
||||
int n; // 当前写入的字节数
|
||||
int off; // 偏移量,表示当前写入的位置在页面中的偏移量
|
||||
int len; // 旧页中的有效数据长度
|
||||
int32 pageno; // 页面号
|
||||
ScanKeyData skey[2]; // 扫描键
|
||||
SysScanDesc sd; // 系统扫描描述符
|
||||
HeapTuple oldtuple = NULL; // 旧元组
|
||||
Form_pg_largeobject olddata = NULL; // 旧的pg_largeobject元组
|
||||
bool neednextpage = true; // 是否需要获取下一个已存在的页面
|
||||
bytea *datafield = NULL; // 数据字段
|
||||
bool pfreeit = false; // 是否需要释放datafield
|
||||
struct {
|
||||
bytea hdr;
|
||||
char data[LOBLKSIZE]; /* make struct big enough */
|
||||
int32 align_it; /* ensure struct is aligned well enough */
|
||||
} workbuf;
|
||||
char* workb = VARDATA(&workbuf.hdr);
|
||||
HeapTuple newtup;
|
||||
Datum values[Natts_pg_largeobject];
|
||||
bool nulls[Natts_pg_largeobject];
|
||||
bool replace[Natts_pg_largeobject];
|
||||
CatalogIndexState indstate;
|
||||
char data[LOBLKSIZE]; /* make struct big enough */
|
||||
int32 align_it; /* ensure struct is aligned well enough */
|
||||
} workbuf; // 工作缓冲区
|
||||
char *workb = VARDATA(&workbuf.hdr); // 缓冲区数据部分的指针
|
||||
HeapTuple newtup; // 新元组
|
||||
Datum values[Natts_pg_largeobject]; // 插入的值
|
||||
bool nulls[Natts_pg_largeobject]; // 是否为NULL
|
||||
bool replace[Natts_pg_largeobject]; // 是否替换
|
||||
CatalogIndexState indstate; // 目录索引状态
|
||||
errno_t rc;
|
||||
|
||||
if (unlikely(!PointerIsValid(obj_desc))) {
|
||||
return 0;
|
||||
}
|
||||
pageno = (int32)(obj_desc->offset / LOBLKSIZE);
|
||||
pageno = (int32)(obj_desc->offset / LOBLKSIZE); // 计算页面号
|
||||
|
||||
rc = memset_s(&workbuf, sizeof(workbuf), 0, sizeof(workbuf));
|
||||
rc = memset_s(&workbuf, sizeof(workbuf), 0, sizeof(workbuf)); // 清零工作缓冲区
|
||||
securec_check(rc, "\0", "\0");
|
||||
Assert(buf != NULL);
|
||||
Assert(buf != NULL); // 断言buf不为空
|
||||
|
||||
check_obj_desc(obj_desc);
|
||||
open_lo_relation();
|
||||
check_obj_desc(obj_desc); // 检查对象描述符
|
||||
open_lo_relation(); // 打开large object关系
|
||||
|
||||
indstate = CatalogOpenIndexes(t_thrd.storage_cxt.lo_heap_r);
|
||||
indstate = CatalogOpenIndexes(t_thrd.storage_cxt.lo_heap_r); // 打开目录索引
|
||||
|
||||
// 初始化扫描键
|
||||
ScanKeyInit(&skey[0], Anum_pg_largeobject_loid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(obj_desc->id));
|
||||
|
||||
ScanKeyInit(&skey[1], Anum_pg_largeobject_pageno, BTGreaterEqualStrategyNumber, F_INT4GE, Int32GetDatum(pageno));
|
||||
|
||||
sd = systable_beginscan_ordered(
|
||||
t_thrd.storage_cxt.lo_heap_r, t_thrd.storage_cxt.lo_index_r, obj_desc->snapshot, 2, skey);
|
||||
// 开始有序扫描
|
||||
sd = systable_beginscan_ordered(t_thrd.storage_cxt.lo_heap_r, t_thrd.storage_cxt.lo_index_r, obj_desc->snapshot, 2,
|
||||
skey);
|
||||
|
||||
while (nwritten < nbytes) {
|
||||
/*
|
||||
|
|
@ -556,8 +610,8 @@ int inv_write(LargeObjectDesc* obj_desc, const char* buf, int nbytes)
|
|||
*
|
||||
* First, load old data into workbuf
|
||||
*/
|
||||
getdatafield(olddata, &datafield, &len, &pfreeit);
|
||||
rc = memcpy_s(workb, len, VARDATA(datafield), len);
|
||||
getdatafield(olddata, &datafield, &len, &pfreeit); // 获取旧数据
|
||||
rc = memcpy_s(workb, len, VARDATA(datafield), len); // 拷贝旧数据到工作缓冲区
|
||||
securec_check(rc, "", "");
|
||||
if (pfreeit)
|
||||
pfree(datafield);
|
||||
|
|
@ -565,25 +619,25 @@ int inv_write(LargeObjectDesc* obj_desc, const char* buf, int nbytes)
|
|||
/*
|
||||
* Fill any hole
|
||||
*/
|
||||
off = (int)(obj_desc->offset % LOBLKSIZE);
|
||||
off = (int)(obj_desc->offset % LOBLKSIZE); // 计算偏移量
|
||||
if (off > len) {
|
||||
rc = memset_s(workb + len, off - len, '\0', off - len);
|
||||
rc = memset_s(workb + len, off - len, '\0', off - len); // 填充空洞
|
||||
securec_check(rc, "", "");
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert appropriate portion of new data
|
||||
*/
|
||||
n = LOBLKSIZE - off;
|
||||
n = (n <= (nbytes - nwritten)) ? n : (nbytes - nwritten);
|
||||
rc = memcpy_s(workb + off, n, buf + nwritten, n);
|
||||
n = LOBLKSIZE - off; // 可写入的长度
|
||||
n = (n <= (nbytes - nwritten)) ? n : (nbytes - nwritten); // 取较小的长度
|
||||
rc = memcpy_s(workb + off, n, buf + nwritten, n); // 拷贝数据到工作缓冲区
|
||||
securec_check(rc, "", "");
|
||||
nwritten += n;
|
||||
obj_desc->offset += n;
|
||||
nwritten += n; // 更新已写入的字节数
|
||||
obj_desc->offset += n; // 更新偏移量
|
||||
off += n;
|
||||
/* compute valid length of new page */
|
||||
len = (len >= off) ? len : off;
|
||||
SET_VARSIZE(&workbuf.hdr, len + VARHDRSZ);
|
||||
SET_VARSIZE(&workbuf.hdr, len + VARHDRSZ); // 设置新页的长度
|
||||
|
||||
/*
|
||||
* Form and insert updated tuple
|
||||
|
|
@ -596,11 +650,11 @@ int inv_write(LargeObjectDesc* obj_desc, const char* buf, int nbytes)
|
|||
securec_check(rc, "", "");
|
||||
values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
|
||||
replace[Anum_pg_largeobject_data - 1] = true;
|
||||
newtup =
|
||||
heap_modify_tuple(oldtuple, RelationGetDescr(t_thrd.storage_cxt.lo_heap_r), values, nulls, replace);
|
||||
simple_heap_update(t_thrd.storage_cxt.lo_heap_r, &newtup->t_self, newtup);
|
||||
CatalogIndexInsert(indstate, newtup);
|
||||
heap_freetuple(newtup);
|
||||
newtup = heap_modify_tuple(oldtuple, RelationGetDescr(t_thrd.storage_cxt.lo_heap_r), values, nulls,
|
||||
replace); // 修改元组
|
||||
simple_heap_update(t_thrd.storage_cxt.lo_heap_r, &newtup->t_self, newtup); // 更新堆中的元组
|
||||
CatalogIndexInsert(indstate, newtup); // 在索引中插入元组
|
||||
heap_freetuple(newtup); // 释放新元组
|
||||
|
||||
/*
|
||||
* We're done with this old page.
|
||||
|
|
@ -614,24 +668,24 @@ int inv_write(LargeObjectDesc* obj_desc, const char* buf, int nbytes)
|
|||
*
|
||||
* First, fill any hole
|
||||
*/
|
||||
off = (int)(obj_desc->offset % LOBLKSIZE);
|
||||
off = (int)(obj_desc->offset % LOBLKSIZE); // 计算偏移量
|
||||
if (off > 0) {
|
||||
rc = memset_s(workb, off, '\0', off);
|
||||
rc = memset_s(workb, off, '\0', off); // 填充空洞
|
||||
securec_check(rc, "", "");
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert appropriate portion of new data
|
||||
*/
|
||||
n = LOBLKSIZE - off;
|
||||
n = (n <= (nbytes - nwritten)) ? n : (nbytes - nwritten);
|
||||
rc = memcpy_s(workb + off, n, buf + nwritten, n);
|
||||
n = LOBLKSIZE - off; // 可写入的长度
|
||||
n = (n <= (nbytes - nwritten)) ? n : (nbytes - nwritten); // 取较小的长度
|
||||
rc = memcpy_s(workb + off, n, buf + nwritten, n); // 拷贝数据到工作缓冲区
|
||||
securec_check(rc, "", "");
|
||||
nwritten += n;
|
||||
obj_desc->offset += n;
|
||||
nwritten += n; // 更新已写入的字节数
|
||||
obj_desc->offset += n; // 更新偏移量
|
||||
/* compute valid length of new page */
|
||||
len = off + n;
|
||||
SET_VARSIZE(&workbuf.hdr, len + VARHDRSZ);
|
||||
SET_VARSIZE(&workbuf.hdr, len + VARHDRSZ); // 设置新页的长度
|
||||
|
||||
/*
|
||||
* Form and insert updated tuple
|
||||
|
|
@ -643,171 +697,142 @@ int inv_write(LargeObjectDesc* obj_desc, const char* buf, int nbytes)
|
|||
values[Anum_pg_largeobject_loid - 1] = ObjectIdGetDatum(obj_desc->id);
|
||||
values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno);
|
||||
values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
|
||||
newtup = heap_form_tuple(t_thrd.storage_cxt.lo_heap_r->rd_att, values, nulls);
|
||||
(void)simple_heap_insert(t_thrd.storage_cxt.lo_heap_r, newtup);
|
||||
CatalogIndexInsert(indstate, newtup);
|
||||
heap_freetuple(newtup);
|
||||
newtup = heap_form_tuple(t_thrd.storage_cxt.lo_heap_r->rd_att, values, nulls); // 创建新元组
|
||||
(void)simple_heap_insert(t_thrd.storage_cxt.lo_heap_r, newtup); // 插入新元组
|
||||
CatalogIndexInsert(indstate, newtup); // 在索引中插入元组
|
||||
heap_freetuple(newtup); // 释放新元组
|
||||
}
|
||||
pageno++;
|
||||
}
|
||||
|
||||
systable_endscan_ordered(sd);
|
||||
systable_endscan_ordered(sd); // 结束有序扫描
|
||||
|
||||
CatalogCloseIndexes(indstate);
|
||||
CatalogCloseIndexes(indstate); // 关闭目录索引
|
||||
|
||||
/*
|
||||
* Advance command counter so that my tuple updates will be seen by later
|
||||
* large-object operations in this transaction.
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
CommandCounterIncrement(); // 提升命令计数器,以确保后续事务可以看到我更新的元组
|
||||
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
void inv_truncate(LargeObjectDesc* obj_desc, int len)
|
||||
void inv_truncate(LargeObjectDesc *obj_desc, int len)
|
||||
{
|
||||
int32 pageno = (int32)(len / LOBLKSIZE);
|
||||
int32 pageno = (int32)(len / LOBLKSIZE); // 计算页号
|
||||
|
||||
int off;
|
||||
ScanKeyData skey[2];
|
||||
SysScanDesc sd;
|
||||
HeapTuple oldtuple;
|
||||
Form_pg_largeobject olddata;
|
||||
|
||||
struct {
|
||||
bytea hdr;
|
||||
char data[LOBLKSIZE]; /* make struct big enough */
|
||||
int32 align_it; /* ensure struct is aligned well enough */
|
||||
char data[LOBLKSIZE]; // 用于存储数据的缓冲区
|
||||
int32 align_it;
|
||||
} workbuf;
|
||||
char* workb = VARDATA(&workbuf.hdr);
|
||||
char *workb = VARDATA(&workbuf.hdr);
|
||||
|
||||
HeapTuple newtup;
|
||||
Datum values[Natts_pg_largeobject] = {0, 0, 0};
|
||||
bool nulls[Natts_pg_largeobject] = {false, false, false};
|
||||
bool nulls[Natts_pg_largeobject] = {false, false, false};
|
||||
bool replace[Natts_pg_largeobject] = {false, false, false};
|
||||
|
||||
CatalogIndexState indstate;
|
||||
errno_t rc;
|
||||
rc = memset_s(&workbuf, sizeof(workbuf), 0, sizeof(workbuf));
|
||||
securec_check(rc, "\0", "\0");
|
||||
Assert(PointerIsValid(obj_desc));
|
||||
|
||||
check_obj_desc(obj_desc);
|
||||
open_lo_relation();
|
||||
rc = memset_s(&workbuf, sizeof(workbuf), 0, sizeof(workbuf)); // 将workbuf缓冲区清零
|
||||
securec_check(rc, "\0", "\0"); // 检查内存操作是否成功
|
||||
|
||||
indstate = CatalogOpenIndexes(t_thrd.storage_cxt.lo_heap_r);
|
||||
Assert(PointerIsValid(obj_desc)); // 检查obj_desc指针是否有效
|
||||
|
||||
/*
|
||||
* Set up to find all pages with desired loid and pageno >= target
|
||||
*/
|
||||
check_obj_desc(obj_desc); // 检查obj_desc的描述是否有效
|
||||
open_lo_relation(); // 打开大型对象关系表
|
||||
|
||||
indstate = CatalogOpenIndexes(t_thrd.storage_cxt.lo_heap_r); // 打开索引
|
||||
|
||||
/* 设置扫描键,查找指定loid和pageno大于等于目标的所有页 */
|
||||
ScanKeyInit(&skey[0], Anum_pg_largeobject_loid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(obj_desc->id));
|
||||
|
||||
ScanKeyInit(&skey[1], Anum_pg_largeobject_pageno, BTGreaterEqualStrategyNumber, F_INT4GE, Int32GetDatum(pageno));
|
||||
|
||||
sd = systable_beginscan_ordered(
|
||||
t_thrd.storage_cxt.lo_heap_r, t_thrd.storage_cxt.lo_index_r, obj_desc->snapshot, 2, skey);
|
||||
sd = systable_beginscan_ordered(t_thrd.storage_cxt.lo_heap_r, t_thrd.storage_cxt.lo_index_r, obj_desc->snapshot, 2,
|
||||
skey); // 开始有序扫描
|
||||
|
||||
/*
|
||||
* If possible, get the page the truncation point is in. The truncation
|
||||
* point may be beyond the end of the LO or in a hole.
|
||||
*/
|
||||
olddata = NULL;
|
||||
if ((oldtuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL) {
|
||||
if (HeapTupleHasNulls(oldtuple)) /* paranoia */
|
||||
if (HeapTupleHasNulls(oldtuple)) // 检查旧元组是否有空值(出于谨慎)
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("null field found in pg_largeobject")));
|
||||
olddata = (Form_pg_largeobject)GETSTRUCT(oldtuple);
|
||||
Assert(olddata->pageno >= pageno);
|
||||
Assert(olddata->pageno >= pageno); // 检查pageno是否大于等于目标页号
|
||||
}
|
||||
|
||||
/*
|
||||
* If we found the page of the truncation point we need to truncate the
|
||||
* data in it. Otherwise if we're in a hole, we need to create a page to
|
||||
* mark the end of data.
|
||||
*/
|
||||
if (olddata != NULL && olddata->pageno == pageno) {
|
||||
/* First, load old data into workbuf */
|
||||
bytea* datafield = NULL;
|
||||
bytea *datafield = NULL;
|
||||
bool pfreeit = false;
|
||||
int pagelen;
|
||||
|
||||
/* 将旧数据加载到workbuf中 */
|
||||
getdatafield(olddata, &datafield, &pagelen, &pfreeit);
|
||||
rc = memcpy_s(workb, pagelen, VARDATA(datafield), pagelen);
|
||||
securec_check(rc, "", "");
|
||||
if (pfreeit)
|
||||
pfree(datafield);
|
||||
|
||||
/*
|
||||
* Fill any hole
|
||||
*/
|
||||
off = len % LOBLKSIZE;
|
||||
|
||||
/* 填充任何空隙 */
|
||||
if (off > pagelen) {
|
||||
rc = memset_s(workb + pagelen, off - pagelen, '\0', off - pagelen);
|
||||
securec_check(rc, "", "");
|
||||
}
|
||||
|
||||
/* compute length of new page */
|
||||
SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ);
|
||||
SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ); // 计算新页的长度
|
||||
|
||||
/*
|
||||
* Form and insert updated tuple
|
||||
*/
|
||||
values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
|
||||
replace[Anum_pg_largeobject_data - 1] = true;
|
||||
newtup = heap_modify_tuple(oldtuple, RelationGetDescr(t_thrd.storage_cxt.lo_heap_r), values, nulls, replace);
|
||||
simple_heap_update(t_thrd.storage_cxt.lo_heap_r, &newtup->t_self, newtup);
|
||||
CatalogIndexInsert(indstate, newtup);
|
||||
heap_freetuple(newtup);
|
||||
newtup = heap_modify_tuple(oldtuple, RelationGetDescr(t_thrd.storage_cxt.lo_heap_r), values, nulls,
|
||||
replace); // 修改旧的元组
|
||||
simple_heap_update(t_thrd.storage_cxt.lo_heap_r, &newtup->t_self, newtup); // 更新堆上的元组
|
||||
CatalogIndexInsert(indstate, newtup); // 插入索引
|
||||
heap_freetuple(newtup); // 释放元组内存
|
||||
} else {
|
||||
/*
|
||||
* If the first page we found was after the truncation point, we're in
|
||||
* a hole that we'll fill, but we need to delete the later page
|
||||
* because the loop below won't visit it again.
|
||||
*/
|
||||
if (olddata != NULL) {
|
||||
Assert(olddata->pageno > pageno);
|
||||
simple_heap_delete(t_thrd.storage_cxt.lo_heap_r, &oldtuple->t_self);
|
||||
simple_heap_delete(t_thrd.storage_cxt.lo_heap_r, &oldtuple->t_self); // 删除后续页面
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a brand new page.
|
||||
*
|
||||
* Fill the hole up to the truncation point
|
||||
*/
|
||||
off = len % LOBLKSIZE;
|
||||
|
||||
/* 填充空隙 */
|
||||
if (off > 0) {
|
||||
rc = memset_s(workb, off, '\0', off);
|
||||
securec_check(rc, "", "");
|
||||
}
|
||||
|
||||
/* compute length of new page */
|
||||
SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ);
|
||||
SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ); // 计算新页的长度
|
||||
|
||||
/*
|
||||
* Form and insert new tuple
|
||||
*/
|
||||
/* 插入新的元组 */
|
||||
values[Anum_pg_largeobject_loid - 1] = ObjectIdGetDatum(obj_desc->id);
|
||||
values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno);
|
||||
values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
|
||||
newtup = heap_form_tuple(t_thrd.storage_cxt.lo_heap_r->rd_att, values, nulls);
|
||||
(void)simple_heap_insert(t_thrd.storage_cxt.lo_heap_r, newtup);
|
||||
CatalogIndexInsert(indstate, newtup);
|
||||
heap_freetuple(newtup);
|
||||
(void)simple_heap_insert(t_thrd.storage_cxt.lo_heap_r, newtup); // 在堆上插入元组
|
||||
CatalogIndexInsert(indstate, newtup); // 插入索引
|
||||
heap_freetuple(newtup); // 释放元组内存
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete any pages after the truncation point. If the initial search
|
||||
* didn't find a page, then of course there's nothing more to do.
|
||||
*/
|
||||
if (olddata != NULL) {
|
||||
while ((oldtuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL) {
|
||||
simple_heap_delete(t_thrd.storage_cxt.lo_heap_r, &oldtuple->t_self);
|
||||
simple_heap_delete(t_thrd.storage_cxt.lo_heap_r, &oldtuple->t_self); // 删除截断点后的页面
|
||||
}
|
||||
}
|
||||
|
||||
systable_endscan_ordered(sd);
|
||||
systable_endscan_ordered(sd); // 结束有序扫描
|
||||
|
||||
CatalogCloseIndexes(indstate);
|
||||
CatalogCloseIndexes(indstate); // 关闭索引
|
||||
|
||||
/*
|
||||
* Advance command counter so that tuple updates will be seen by later
|
||||
* large-object operations in this transaction.
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
CommandCounterIncrement(); // 提升命令计数器,以便后续事务中的元组更新可见
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue