839 lines
34 KiB
C++
839 lines
34 KiB
C++
/* -------------------------------------------------------------------------
|
||
*
|
||
* 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(); // 提升命令计数器,以便后续事务中的元组更新可见
|
||
}
|