华为openGauss数据库源码解析——Page操作(2)

针对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);
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容