openGauss-server/src/gausskernel/storage/large_object/inv_api.cpp

839 lines
34 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -------------------------------------------------------------------------
*
* inv_api.cpp
* routines for manipulating inversion fs large objects. This file
* contains the user-level large object application interface routines.
*
*
* Note: we access pg_largeobject.data using its C struct declaration.
* This is safe because it immediately follows pageno which is an int4 field,
* and therefore the data field will always be 4-byte aligned, even if it
* is in the short 1-byte-header format. We have to detoast it since it's
* quite likely to be in compressed or short format. We also need to check
* for NULLs, since initdb will mark loid and pageno but not data as NOT NULL.
*
* Note: many of these routines leak memory in CurrentMemoryContext, as indeed
* does most of the backend code. We expect that CurrentMemoryContext will
* be a short-lived context. Data that must persist across function calls
* is kept either in u_sess->cache_mem_cxt (the Relation structs) or in the
* memory context given to inv_open (for LargeObjectDesc structs).
*
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/gausskernel/storage/large_object/inv_api.cpp
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include "access/tableam.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/sysattr.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
#include "libpq/libpq-fs.h"
#include "miscadmin.h"
#include "storage/large_object.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/rel_gs.h"
#include "utils/snapmgr.h"
#include "access/heapam.h"
/*
* Open pg_largeobject and its index, if not already done in current xact
*/
static void open_lo_relation(void)
{
ResourceOwner currentOwner;
if (t_thrd.storage_cxt.lo_heap_r && t_thrd.storage_cxt.lo_index_r)
return; /* already open in current xact */
/* 获取当前的资源拥有者 */
currentOwner = t_thrd.utils_cxt.CurrentResourceOwner;
PG_TRY();
{
/* 将顶层事务的资源拥有者设置为当前资源拥有者 */
t_thrd.utils_cxt.CurrentResourceOwner = t_thrd.utils_cxt.TopTransactionResourceOwner;
/* 使用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();
{
/* 在错误发生时恢复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) { // 如果lo_heap_r或lo_index_r不为空
/*
* 只有在提交事务时才关闭,否则由于中止清理处理
*/
if (isCommit) { // 如果是提交事务
ResourceOwner currentOwner; // 当前资源拥有者
currentOwner = t_thrd.utils_cxt.CurrentResourceOwner; // 保存当前资源拥有者
PG_TRY(); // 尝试执行以下代码块
{
t_thrd.utils_cxt.CurrentResourceOwner =
t_thrd.utils_cxt.TopTransactionResourceOwner; // 设置当前资源拥有者为顶级事务资源拥有者
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(); // 捕获异常
{
/* 确保错误时恢复CurrentResourceOwner */
t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; // 恢复当前资源拥有者
PG_RE_THROW(); // 重新抛出异常
}
PG_END_TRY();
t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; // 恢复当前资源拥有者
}
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; // 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
// 结束系统扫描
systable_endscan(sd);
// 关闭pg_lo_meta关系释放AccessShareLock锁定的资源
heap_close(pg_lo_meta, AccessShareLock);
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)
{
bytea *datafield = NULL; // 数据域指针
int len; // 数据域长度
bool freeit = false; // 是否需要释放数据域
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; // 返回是否需要释放数据域
}
/*
* inv_create -- create a new large object
*
* Arguments:
* lobjId - OID to use for new large object, or InvalidOid to pick one
*
* Returns:
* OID of new object
*
* If lobjId is not InvalidOid, then an error occurs if the OID is already
* in use.
*/
Oid inv_create(Oid lobjId)
{
Oid lobjId_new;
/*
* Create a new largeobject with empty data pages
创建一个新的largeobject型对象并设置为空数据页。
*/
lobjId_new = LargeObjectCreate(lobjId);
/*
* dependency on the owner of largeobject
*
* The reason why we use LargeObjectRelationId instead of
* LargeObjectMetadataRelationId here is to provide backward compatibility
* to the applications which utilize a knowledge about internal layout of
* system catalogs. OID of pg_largeobject_metadata and loid of
* pg_largeobject are same value, so there are no actual differences here.
*/
recordDependencyOnOwner(LargeObjectRelationId, lobjId_new, GetUserId());
/* Post creation hook for new large object */
InvokeObjectAccessHook(OAT_POST_CREATE, LargeObjectRelationId, lobjId_new, 0, NULL);
/*
* Advance command counter to make new tuple visible to later operations.
*/
CommandCounterIncrement();
return lobjId_new;
}
/*
* inv_open -- access an existing large object.
*
* Returns:
* Large object descriptor, appropriately filled in. The descriptor
* 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 *retval = NULL; // 返回的LargeObjectDesc型对象描述符指针
retval =
(LargeObjectDesc *)MemoryContextAlloc(mcxt, sizeof(LargeObjectDesc)); // 分配内存空间用于存储LargeObjectDesc型对象描述符
retval->id = lobjId; // 设置LargeObjectDesc型对象的OID
retval->subid = GetCurrentSubTransactionId(); // 设置当前子事务ID
retval->offset = 0; // 设置偏移量
if (flags & INV_WRITE) { // 如果标志包含INV_WRITE表示以写模式打开LargeObjectDesc型对象
retval->snapshot = SnapshotNow; // 使用当前快照
retval->flags = IFS_WRLOCK | IFS_RDLOCK; // 设置写锁和读锁标志
} else if (flags & INV_READ) { // 如果标志包含INV_READ表示以读模式打开LargeObjectDesc型对象
/*
* 必须在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))); // 抛出错误,标志无效
}
// 不能使用LargeObjectExists因为它总是使用SnapshotNow
if (!myLargeObjectExists(lobjId, retval->snapshot)) { // 如果大型对象不存在
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("large object %u does not exist", lobjId))); // 抛出错误,大型对象不存在
}
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)
{
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!)
*
* returns -1 if failed
*/
int inv_drop(Oid lobjId)
{
ObjectAddress object;
/*
* Delete any comments and dependencies on the large object
*/
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();// 提升命令计数器
return 1;// 返回1表示删除成功
}
/*
* Determine size of a large object
*
* NOTE: LOs can contain gaps, just like Unix files. We actually return
* the offset of the last byte + 1.
*/
static uint32 inv_getsize(LargeObjectDesc* obj_desc)
{
uint32 lastbyte = 0;
ScanKeyData skey[1];
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));
/*
* 有序扫描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
* constrain only loid, a backwards scan should visit all pages of the
* large object in reverse pageno order. So, it's sufficient to examine
* the first valid tuple (== last valid page).
*/
tuple = systable_getnext_ordered(sd, BackwardScanDirection);
if (HeapTupleIsValid(tuple)) {
Form_pg_largeobject data;
bytea* datafield = NULL;
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)
{
Assert(PointerIsValid(obj_desc)); // 断言大型对象描述符指针有效
switch (whence) {
case SEEK_SET: // 从文件开头开始计算offset
if (offset < 0) // 如果offset为负数则抛出错误
ereport(ERROR, (errcode_for_file_access(), errmsg("invalid seek offset: %d", offset)));
obj_desc->offset = offset; // 设置偏移量为offset
break;
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; // 偏移量增加offset
break;
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; // 偏移量设置为对象大小加offset
} break;
default:
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_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
{
Assert(PointerIsValid(obj_desc)); // 断言大型对象描述符指针有效
int nread = 0; // 已读取的字节数
int n;
int off;
int len;
int32 pageno = (int32)(obj_desc->offset / LOBLKSIZE); // 根据偏移量计算页号
uint32 pageoff;
ScanKeyData skey[2];
SysScanDesc sd;
HeapTuple tuple;
errno_t rc = EOK;
Assert(buf != NULL); // 断言缓冲区指针有效
if (nbytes <= 0) { // 如果要读取的字节数小于等于0则直接返回0
return 0;
}
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);
while ((tuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL) {
Form_pg_largeobject data;
bytea *datafield = NULL;
bool pfreeit = false;
if (HeapTupleHasNulls(tuple)) // 如果元组有空字段,抛出错误
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("null field found in pg_largeobject")));
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) { // 如果页的偏移量大于当前偏移量,则说明存在缺失的部分,将其读出为零
n = pageoff - obj_desc->offset;
n = (n <= (nbytes - nread)) ? n : (nbytes - nread);
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)));
}
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); // 将数据复制到buf中
securec_check(rc, "", "");
nread += n;
obj_desc->offset += n;
}
if (pfreeit)
pfree(datafield); // 释放大型对象数据字段的内存
}
if (nread >= nbytes)
break;
}
systable_endscan_ordered(sd); // 结束扫描
return nread; // 返回已读取的字节数
}
void check_obj_desc(const LargeObjectDesc* obj_desc)
{
/* enforce writability because snapshot is probably wrong otherwise */
if ((obj_desc->flags & IFS_WRLOCK) == 0)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("large object %u was not opened for writing", obj_desc->id)));
/* check existence of the target largeobject */
if (!LargeObjectExists(obj_desc->id))
ereport(
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)
{
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; // 旧的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]; // 是否为NULL
bool replace[Natts_pg_largeobject]; // 是否替换
CatalogIndexState indstate; // 目录索引状态
errno_t rc;
if (unlikely(!PointerIsValid(obj_desc))) {
return 0;
}
pageno = (int32)(obj_desc->offset / LOBLKSIZE); // 计算页面号
rc = memset_s(&workbuf, sizeof(workbuf), 0, sizeof(workbuf)); // 清零工作缓冲区
securec_check(rc, "\0", "\0");
Assert(buf != NULL); // 断言buf不为空
check_obj_desc(obj_desc); // 检查对象描述符
open_lo_relation(); // 打开large object关系
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);
while (nwritten < nbytes) {
/*
* If possible, get next pre-existing page of the LO. We expect the
* indexscan will deliver these in order --- but there may be holes.
*/
if (neednextpage) {
if ((oldtuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL) {
if (HeapTupleHasNulls(oldtuple)) /* paranoia */
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("null field found in pg_largeobject")));
olddata = (Form_pg_largeobject)GETSTRUCT(oldtuple);
Assert(olddata->pageno >= pageno);
}
neednextpage = false;
}
/*
* If we have a pre-existing page, see if it is the page we want to
* write, or a later one.
*/
if (olddata != NULL && olddata->pageno == pageno) {
/*
* Update an existing page with fresh data.
*
* First, load old data into workbuf
*/
getdatafield(olddata, &datafield, &len, &pfreeit); // 获取旧数据
rc = memcpy_s(workb, len, VARDATA(datafield), len); // 拷贝旧数据到工作缓冲区
securec_check(rc, "", "");
if (pfreeit)
pfree(datafield);
/*
* Fill any hole
*/
off = (int)(obj_desc->offset % LOBLKSIZE); // 计算偏移量
if (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); // 拷贝数据到工作缓冲区
securec_check(rc, "", "");
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); // 设置新页的长度
/*
* Form and insert updated tuple
*/
rc = memset_s(values, sizeof(values), 0, sizeof(values));
securec_check(rc, "", "");
rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls));
securec_check(rc, "", "");
rc = memset_s(replace, sizeof(replace), false, sizeof(replace));
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); // 释放新元组
/*
* We're done with this old page.
*/
oldtuple = NULL;
olddata = NULL;
neednextpage = true;
} else {
/*
* Write a brand new page.
*
* First, fill any hole
*/
off = (int)(obj_desc->offset % LOBLKSIZE); // 计算偏移量
if (off > 0) {
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); // 拷贝数据到工作缓冲区
securec_check(rc, "", "");
nwritten += n; // 更新已写入的字节数
obj_desc->offset += n; // 更新偏移量
/* compute valid length of new page */
len = off + n;
SET_VARSIZE(&workbuf.hdr, len + VARHDRSZ); // 设置新页的长度
/*
* Form and insert updated tuple
*/
rc = memset_s(values, sizeof(values), 0, sizeof(values));
securec_check(rc, "", "");
rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls));
securec_check(rc, "", "");
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); // 释放新元组
}
pageno++;
}
systable_endscan_ordered(sd); // 结束有序扫描
CatalogCloseIndexes(indstate); // 关闭目录索引
/*
* Advance command counter so that my tuple updates will be seen by later
* large-object operations in this transaction.
*/
CommandCounterIncrement(); // 提升命令计数器,以确保后续事务可以看到我更新的元组
return nwritten;
}
void inv_truncate(LargeObjectDesc *obj_desc, int len)
{
int32 pageno = (int32)(len / LOBLKSIZE); // 计算页号
int off;
ScanKeyData skey[2];
SysScanDesc sd;
HeapTuple oldtuple;
Form_pg_largeobject olddata;
struct {
bytea hdr;
char data[LOBLKSIZE]; // 用于存储数据的缓冲区
int32 align_it;
} workbuf;
char *workb = VARDATA(&workbuf.hdr);
HeapTuple newtup;
Datum values[Natts_pg_largeobject] = {0, 0, 0};
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)); // 将workbuf缓冲区清零
securec_check(rc, "\0", "\0"); // 检查内存操作是否成功
Assert(PointerIsValid(obj_desc)); // 检查obj_desc指针是否有效
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); // 开始有序扫描
olddata = NULL;
if ((oldtuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL) {
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); // 检查pageno是否大于等于目标页号
}
if (olddata != NULL && olddata->pageno == pageno) {
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);
off = len % LOBLKSIZE;
/* 填充任何空隙 */
if (off > pagelen) {
rc = memset_s(workb + pagelen, off - pagelen, '\0', off - pagelen);
securec_check(rc, "", "");
}
SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ); // 计算新页的长度
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); // 释放元组内存
} else {
if (olddata != NULL) {
Assert(olddata->pageno > pageno);
simple_heap_delete(t_thrd.storage_cxt.lo_heap_r, &oldtuple->t_self); // 删除后续页面
}
off = len % LOBLKSIZE;
/* 填充空隙 */
if (off > 0) {
rc = memset_s(workb, off, '\0', off);
securec_check(rc, "", "");
}
SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ); // 计算新页的长度
/* 插入新的元组 */
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); // 释放元组内存
}
if (olddata != NULL) {
while ((oldtuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL) {
simple_heap_delete(t_thrd.storage_cxt.lo_heap_r, &oldtuple->t_self); // 删除截断点后的页面
}
}
systable_endscan_ordered(sd); // 结束有序扫描
CatalogCloseIndexes(indstate); // 关闭索引
CommandCounterIncrement(); // 提升命令计数器,以便后续事务中的元组更新可见
}