针对openGauss数据库中的page相关操作的函数进行分析,对函数的功能进行总体概括,同时针对函数内部各行代码进行解释说明
将临时页面复制回永久页并释放临时页
//将临时页复制回永久页并释放临时页。
void PageRestoreTempPage(Page tempPage, Page oldPage)
{
Size pageSize;
errno_t rc = EOK;
pageSize = PageGetPageSize(tempPage);
rc = memcpy_s((char *)oldPage, pageSize, (char *)tempPage, pageSize);
securec_check(rc, "", "");
pfree(tempPage);
}
释放页面碎片空间
//释放页面上的碎片空间。它不会删除未使用的行指针!
//这个函数宏观上的整体过程是:统计一个页面中的“有内容”的元组项,接着把这些元组项“装入”到一个itemidbase的数组,借助这个辅助空间,
//移动这些元组,使得他们的页面偏移量连续起来,消除页面中的碎片,得到更多的剩余空间。
void PageRepairFragmentation(Page page)
{
//先获取偏移量
Offset pdLower = ((PageHeader)page)->pd_lower;
Offset pdUpper = ((PageHeader)page)->pd_upper;
Offset pdSpecial = ((PageHeader)page)->pd_special;
ItemId lp;
int nline, nstorage, nunused;
int i;
Size totallen;
Offset upper;
errno_t rc = EOK;
/*
* It's worth the trouble to be more paranoid here than in most places,
* because we are about to reshuffle data in (what is usually) a shared
* disk buffer. If we aren't careful then corrupted pointers, lengths,
* etc could cause us to clobber adjacent disk buffers, spreading the data
* loss further. So, check everything.
*/
//首先判断当前页面page是否有效,如果无效的话,报错,函数退出
if ((unsigned int)(pdLower) < SizeOfPageHeaderData || pdLower > pdUpper || pdUpper > pdSpecial ||
pdSpecial > BLCKSZ || (unsigned int)(pdSpecial) != MAXALIGN(pdSpecial))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
errmsg("corrupted page pointers: lower = %d, upper = %d, special = %d", pdLower, pdUpper, pdSpecial)));
//nline为页面中元组项的个数
nline = PageGetMaxOffsetNumber(page);
nunused = nstorage = 0;
//FirstOffsetNumber = 1
for (i = FirstOffsetNumber; i <= nline; i++) {
//根据偏移量获取linepoint
lp = PageGetItemId(page, i);
if (ItemIdIsUsed(lp)) {
if (ItemIdHasStorage(lp))
//linepoint正在使用
nstorage++;
} else {
/* Unused entries should have lp_len = 0, but make sure */
//linepoint已经失效
ItemIdSetUnused(lp);
nunused++;
}
}
//两个变量nstorage,nunused分别用于统计页面中元组项已经使用和未使用的数量值。
if (nstorage == 0) {
//页面全部为空,不需要清除碎片,只需要重置它的值便可以
/* Page is completely empty, so just reset it quickly */
((PageHeader)page)->pd_upper = pdSpecial;
} else {
/* Need to compact the page the hard way */
/*
typedef struct itemIdSortData {
// int offsetindex;
// int itemoff;
// Size alignedlen;
// ItemIdData olditemid;
//}
typedef itemIdSortData* itemIdSort;
*/
itemIdSortData itemidbase[MaxHeapTuplesPerPage];
itemIdSort itemidptr = itemidbase; //指针指向itemidbase数组
totallen = 0;
//遍历所有的元组项,如果元组项item已被存储,将itemidptr指向这个元组项,然后itemidptr自增
for (i = 0; i < nline; i++) {
lp = PageGetItemId(page, i + 1);
if (ItemIdHasStorage(lp)) {
itemidptr->offsetindex = i;
itemidptr->itemoff = ItemIdGetOffset(lp);
if (itemidptr->itemoff < (int)pdUpper || itemidptr->itemoff >= (int)pdSpecial)
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED),
errmsg("corrupted item pointer: %d", itemidptr->itemoff)));
itemidptr->alignedlen = MAXALIGN(ItemIdGetLength(lp));
totallen += itemidptr->alignedlen;
itemidptr++;
}
}
if (totallen > (Size)(pdSpecial - pdLower))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED), errmsg("corrupted item lengths: total %u, available space %d",
(unsigned int)totallen, pdSpecial - pdLower)));
/* sort itemIdSortData array into decreasing itemoff order */
//对itemIdSortData数组按偏移量进行降序排序
qsort((char *)itemidbase, nstorage, sizeof(itemIdSortData), ItemOffCompare);
/* compactify page */
upper = pdSpecial;
// upper首先被赋值为page的特殊空间起始偏移量,接下来遍历itemIdSortData类型的数组itemidbase
// 每次操作一个元组项,upper减掉此元组项对应的长度
/*
接下来调用memmove函数,将itemidbase中的元组项,移到页面中的upper位置,相当于“消除”了碎片。
这个算法类似于操作系统中的存储管理中的“紧凑”操作,将分散的元组项地址连续起来,消除页面中的内部碎片。
*/
for (i = 0, itemidptr = itemidbase; i < nstorage; i++, itemidptr++) {
lp = PageGetItemId(page, itemidptr->offsetindex + 1);
upper -= itemidptr->alignedlen;
rc = memmove_s((char *)page + upper, itemidptr->alignedlen, (char *)page + itemidptr->itemoff,
itemidptr->alignedlen);
securec_check(rc, "", "");
lp->lp_off = upper;
}
((PageHeader)page)->pd_upper = upper;
}
/* Set hint bit for PageAddItem */
if (nunused > 0)
//设置有空闲linepoint
PageSetHasFreeLinePointers(page);
else
//设置没有空闲linepoint
PageClearHasFreeLinePointers(page);
}
获取页面剩余空间
//返回页面page中的剩余空间,定义一个变量space为page的pd_lower和pd_upper之差
//如果space小于itemIdData的存储空间时,便返回0,说明无剩余空间了,否则返回space-sizeof(itemIdData)
Size PageGetFreeSpace(Page page)
{
int space;
/*
* Use signed arithmetic here so that we behave sensibly if pd_lower >
* pd_upper.
*/
space = (int)((PageHeader)page)->pd_upper - (int)((PageHeader)page)->pd_lower;
if (space < (int)sizeof(ItemIdData))
return 0;
space -= sizeof(ItemIdData);
return (Size)space;
}
//返回空闲空间大小
Size PageGetExactFreeSpace(Page page)
{
int space;
/*
* Use signed arithmetic here so that we behave sensibly if pd_lower >
* pd_upper.
*/
space = (int)((PageHeader)page)->pd_upper - (int)((PageHeader)page)->pd_lower;
if (space < 0) {
return 0;
}
return (Size)(uint32)space;
}
Size PageGetHeapFreeSpace(Page page)
{
Size space;
space = PageGetFreeSpace(page);
if (space > 0) {
OffsetNumber offnum, nline;
/*
* Are there already MaxHeapTuplesPerPage line pointers in the page?
*/
//获取页面上的元组数
nline = PageGetMaxOffsetNumber(page);
//判断元组数是否大于设定的页面最大元组数
if (nline >= MaxHeapTuplesPerPage) {
if (PageHasFreeLinePointers((PageHeader)page)) {
/*
* Since this is just a hint, we must confirm that there is
* indeed a free line pointer
*/
//PageHasFreeLinePointers()函数只是一个提示,还需要去确认是否确实存在free line point
for (offnum = FirstOffsetNumber; offnum <= nline; offnum = OffsetNumberNext(offnum)) {
ItemId lp = PageGetItemId(page, offnum);
if (!ItemIdIsUsed(lp))
break;
}
if (offnum > nline) {
/*
* The hint is wrong, but we can't clear it here since we
* don't have the ability to mark the page dirty.
*/
space = 0;
}
} else {
/*
* Although the hint might be wrong, PageAddItem will believe
* it anyway, so we must believe it too.
*/
space = 0;
}
}
}
return space;
}
删除页面page(存储的是索引元组)删除offnum处的元组项item
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
批量删除页面中的itemnos数组,数组长度为nitems
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
通过dictSize进行页面初始化(提前设置special部分的长度进行页面初始化)
//通过dictSize进行页面初始化
void PageReinitWithDict(Page page, Size dictSize)
{
HeapPageHeader pd;
/* re-init this page with dictionary area */
//assert(false)程序终止
Assert(PageIsEmpty(page));
PageSetCompressed(page);
pd = (HeapPageHeader)page;
pd->pd_upper = PageGetPageSize(page) - MAXALIGN(dictSize);
pd->pd_special = pd->pd_upper;
}
清理页面的special部分
bool PageFreeDict(Page page)
{
Assert(PageIsCompressed(page));
/* case 1: free page dictionary directly if page is empty. */
if (PageIsEmpty(page)) {
PageHeader pd;
pd = (PageHeader)page;
//清理页面的special部分
pd->pd_upper = pd->pd_special;
PageClearCompressed(page);
return true;
}
/* case 2: check all tuples in page, and free page dictionary if there
* are no compressed tuples. */
if (PageIsAllVisible(page)) {
return PageCheckAndFreeDict(page);
}
return false;
}
设置页面checksum
void PageSetChecksumInplace(Page page, BlockNumber blkno)
{
/* If we don't need a checksum, just return */
if (PageIsNew(page)) {
return;
}
/* set page->pd_flags mark using FNV1A for checksum */
PageSetChecksumByFNV1A(page);
((PageHeader)page)->pd_checksum = pg_checksum_page((char*)page, blkno);
}
更新页面版本
/* Upgrade the page from PG_PAGE_4B_LAYOUT_VERSION(4) to PG_PAGE_LAYOUT_VERSION(5) */
void PageLocalUpgrade(Page page)
{
PageHeader phdr = (PageHeader)page;
errno_t rc = EOK;
Size movesize = phdr->pd_lower - SizeOfPageHeaderData;
Assert(PageIs4BXidVersion(page));
if (movesize > 0) {
rc = memmove_s((char*)page + SizeOfHeapPageHeaderData,
phdr->pd_upper - SizeOfHeapPageHeaderData,
(char*)page + SizeOfPageHeaderData,
phdr->pd_lower - SizeOfPageHeaderData);
securec_check(rc, "", "");
}
/* Update PageHeaderInfo */
phdr->pd_lower += SizeOfHeapPageUpgradeData;
upgrade_page_ver_4_to_5(page);
}