海山数据库(He3DB)源码详解:海山PG 表和元组的组织方式(2)
一、页的操作
1、页面初始化
访问Page
时会先将它加载到内存,所以Page
可以仅用一个char *
类型的指针来表示,指向内存中该Page
的起始位置。由于Page
的大小是已知的,通过Page
指针和Page
的大小即可表示并访问一个Page
。在构建一个Page
时,会调用PageInit
函数进行初始化。
void
PageInit(Page page, Size pageSize, Size specialSize)
{
// p指向Page的头部的起始位置,也是整个Page的起始位置
PageHeader p = (PageHeader) page;
// 对special区域的大小进行对齐
specialSize = MAXALIGN(specialSize);
// Page的大小应该为常量BLCKSZ(默认是8192)
Assert(pageSize == BLCKSZ);
// 除了头部和special区域外,Page内还应该有可用空间
Assert(pageSize > specialSize + SizeOfPageHeaderData);
// 将整个Page的内容填充为0
MemSet(p, 0, pageSize);
// 初始化Page头部的一些字段
p->pd_flags = 0;
p->pd_lower = SizeOfPageHeaderData;
p->pd_upper = pageSize - specialSize;
p->pd_special = pageSize - specialSize;
PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION);
/* p->pd_prune_xid = InvalidTransactionId; done by above MemSet */
}
- 页面初始化函数:流程首先判断参数是否正确,即
pageSize
是否等于BLCKSZ
(8KB),之后对specialSize
的大小做判断 - 初始化页面头部信息中的字段,如
pd->flags
、pd->lower
、pd->upper
、pd->special
以及pd->pagesize_version
等标志位
2、检验页面有效性
bool
PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags)
{
PageHeader p = (PageHeader) page;
size_t *pagebytes = NULL;
int i = 0;
bool checksum_failure = false;
bool header_sane = false;
bool all_zeroes = false;
uint16 checksum = 0;
if (!PageIsNew(page))
{
if (DataChecksumsEnabled())
{
checksum = pg_checksum_page((char *) page, blkno);
if (checksum != p->pd_checksum) {
checksum_failure = true;
}
}
if ((p->pd_flags & ~PD_VALID_FLAG_BITS) == 0 && p->pd_lower <= p->pd_upper && p->pd_upper <= p->pd_special && p->pd_special <= BLCKSZ && p->pd_special == MAXALIGN(p->pd_special)) {
header_sane = true;
}
if (header_sane && !checksum_failure) {
LOG_FUNCTION_EXIT();
return true;
}
}
all_zeroes = true;
pagebytes = (size_t *) page;
for (i = 0; i < (BLCKSZ / sizeof(size_t)); i++)
{
if (pagebytes[i] != 0)
{
all_zeroes = false;
break;
}
}
if (all_zeroes) {
LOG_FUNCTION_EXIT();
return true;
}
if (checksum_failure)
{
if ((flags & PIV_LOG_WARNING) != 0) {
ereport(WARNING,
(errcode(ERRCODE_DATA_CORRUPTED), errmsg("page verification failed, calculated checksum %u but expected %u", checksum, p->pd_checksum)));
}
if ((flags & PIV_REPORT_STAT) != 0) {
pgstat_report_checksum_failure();
}
if (header_sane && ignore_checksum_failure) {
LOG_FUNCTION_EXIT();
return true;
}
}
return false;
}
- 函数的作用是检查页面头部信息和检验和是否有效
- 首先判断页面的校验和功能是否开启,如果开启,使用
pg_checksum_page()
函数计算页面的校验和并和页面头部信息中存储的校验和做对比 - 计算页面头部信息的那些字段是否符合常理
- 为了效率上的优化,不对全零页面做检测
二、表的操作
表的打开并不是物理的打开文件,而是返回表的RelationData
结构体,核心就是两个函数:
1、relation_open
根据表的OID
和lockmode
来获得表的RealtionData
结构体并加锁,返回relationData
。如果是第一次打开,会在RelCache
中创建一个新的RelationData
结构体。
Relation
relation_open(Oid relationId, LOCKMODE lockmode)
{
Relation r;
Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
if (lockmode != NoLock) {
LockRelationOid(relationId, lockmode);
}
r = RelationIdGetRelation(relationId);
if (!RelationIsValid(r)) {
elog(ERROR, "could not open relation with OID %u", relationId);
}
Assert(lockmode != NoLock ||
IsBootstrapProcessingMode() ||
CheckRelationLockedByMe(r, AccessShareLock, true));
if (RelationUsesLocalBuffers(r)) {
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
}<