PG13源码阅读--001插入数据

PG13源码阅读–001插入数据

PG版本:13.5

源码下载地址:https://www.postgresql.org/ftp/source/v13.5/

源码位置:src\backend\storage\page\bufpage.c

参考链接:http://blog.itpub.net/6906/viewspace-2374915/

1、源码分析

插入数据主要的实现在bufpage.c,主要的函数是PageAddItemExtended。

变量、宏定义和结构体等说明

1)Page

指向char的指针

typedef char *Pointer;
typedef Pointer Page;

2)Item

指向char的指针

typedef char *Pointer;
typedef Pointer Item;

3)Size

typedef size_t Size;

4)OffsetNumber

typedef unsigned short uint16;
typedef uint16 OffsetNumber;

5)PageHeader

typedef struct PageHeaderData
{
	/* XXX LSN is member of *any* block, not only page-organized ones */
	PageXLogRecPtr pd_lsn;		/* LSN: next byte after last byte of xlog
								 * record for last change to this page */
	uint16		pd_checksum;	/* checksum */
	uint16		pd_flags;		/* flag bits, see below */
	LocationIndex pd_lower;		/* offset to start of free space */
	LocationIndex pd_upper;		/* offset to end of free space */
	LocationIndex pd_special;	/* offset to start of special space */
	uint16		pd_pagesize_version;
	TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */
	ItemIdData	pd_linp[FLEXIBLE_ARRAY_MEMBER]; /* line pointer array */
} PageHeaderData;

typedef PageHeaderData *PageHeader;
#define PD_HAS_FREE_LINES	0x0001	/* are there any unused line pointers? */
#define PD_PAGE_FULL		0x0002	/* not enough free space for new tuple? */
#define PD_ALL_VISIBLE		0x0004	/* all tuples on page are visible to
									 * everyone */

#define PD_VALID_FLAG_BITS	0x0007	/* OR of all valid pd_flags bits */

6)SizeOfPageHeaderData

长整型,定义页头大小:

#define offsetof(type, field)	((long) &((type *)0)->field)
#define SizeOfPageHeaderData (offsetof(PageHeaderData, pd_linp))

7)BLCKSZ

#define BLCKSZ 8192

8)PageGetMaxOffsetNumber

如果空闲空间的lower小于等于页头大小,值为0,否则值为lower减去页头大小(24Bytes)后除以ItemId大小(4Bytes)

#define PageGetMaxOffsetNumber(page) \
	(((PageHeader) (page))->pd_lower <= SizeOfPageHeaderData ? 0 : \
	 ((((PageHeader) (page))->pd_lower - SizeOfPageHeaderData) \
	  / sizeof(ItemIdData)))

9)InvalidOffsetNumber

是否无效的偏移值

#define InvalidOffsetNumber		((OffsetNumber) 0)

10)OffsetNumberNext

输入值+1

#define OffsetNumberNext(offsetNumber) \
	((OffsetNumber) (1 + (offsetNumber)))

11)ItemId

结构体ItemIdData指针

typedef struct ItemIdData
{
	unsigned	lp_off:15,		/* offset to tuple (from start of page) */
				lp_flags:2,		/* state of line pointer, see below */
				lp_len:15;		/* byte length of tuple */
} ItemIdData;

typedef ItemIdData *ItemId;
#define LP_UNUSED		0		/* unused (should always have lp_len=0) */
#define LP_NORMAL		1		/* used (should always have lp_len>0) */
#define LP_REDIRECT		2		/* HOT redirect (should have lp_len=0) */
#define LP_DEAD			3		/* dead, may or may not have storage */

12)PageGetItemId

获取相应的ItemIdData的指针

#define PageGetItemId(page, offsetNumber) \
	((ItemId) (&((PageHeader) (page))->pd_linp[(offsetNumber) - 1]))

13)PAI_OVERWRITE

位标记,实际值为1

#define PAI_OVERWRITE			(1 << 0)

14)PAI_IS_HEAP

位标记,实际值为2

#define PAI_IS_HEAP				(1 << 1)

15)ItemIdIsUsed

ItemId是否已被占用

#define ItemIdIsUsed(itemId) \
	((itemId)->lp_flags != LP_UNUSED)

16)ItemIdHasStorage

#define ItemIdHasStorage(itemId) \
	((itemId)->lp_len != 0)

17)PageHasFreeLinePointers、PageClearHasFreeLinePointers

PageHasFreeLinePointers判断Page从Header到Lower之间是否有空位

PageClearHasFreeLinePointers清除空位标记(标记错误时清除)

#define PageHasFreeLinePointers(page) \
	(((PageHeader) (page))->pd_flags & PD_HAS_FREE_LINES)
#define PageClearHasFreeLinePointers(page) \
	(((PageHeader) (page))->pd_flags &= ~PD_HAS_FREE_LINES)

18)MaxHeapTuplesPerPage

每个Page可以容纳的最大Tuple数,计算公式是:

(Block大小 - 页头大小)/(对齐后行头部大小 + 行指针大小)

#define MaxHeapTuplesPerPage	\
	((int) ((BLCKSZ - SizeOfPageHeaderData) / \
			(MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))))

PageAddItemExtended函数

/*
PageAddItemExtended函数:
输入:
  page-指向页的指针
  item-指向数据的指针
  size-数据大小
  offsetNumber-指定数据存储的偏移量
  flags-标记位(是否覆盖/是否Heap数据)
输出:
  OffsetNumber-数据存储实际的偏移量
*/
OffsetNumber
PageAddItemExtended(Page page,
					Item item,
					Size size,
					OffsetNumber offsetNumber,
					int flags)
{
	PageHeader	phdr = (PageHeader) page;//页头指针
	Size		alignedSize;//对齐大小
	int			lower;//Free space低位
	int			upper;//Free space高位
	ItemId		itemId;//行指针
	OffsetNumber limit;//行偏移,Free space中第一个可用的位置偏移
	bool		needshuffle = false;//是否需要移动原有数据

	/*
	 * Be wary about corrupted page pointers
	 */
	if (phdr->pd_lower < SizeOfPageHeaderData ||
		phdr->pd_lower > phdr->pd_upper ||
		phdr->pd_upper > phdr->pd_special ||
		phdr->pd_special > BLCKSZ)
		ereport(PANIC,
				(errcode(ERRCODE_DATA_CORRUPTED),
				 errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
						phdr->pd_lower, phdr->pd_upper, phdr->pd_special)));

	/*
	 * Select offsetNumber to place the new item at
	 */
    //获取存储数据的偏移(位于Lower和Upper之间)
	limit = OffsetNumberNext(PageGetMaxOffsetNumber(page));

	/* was offsetNumber passed in? */
	if (OffsetNumberIsValid(offsetNumber))
	{
        //如果指定了数据存储的偏移量(传入的偏移量参数有效)
		/* yes, check it */
		if ((flags & PAI_OVERWRITE) != 0)//不覆盖原有数据
		{
			if (offsetNumber < limit)
			{
                //获取指定偏移的ItemId
				itemId = PageGetItemId(phdr, offsetNumber);
                //指定的数据偏移已使用或者已分配存储空间,报错
				if (ItemIdIsUsed(itemId) || ItemIdHasStorage(itemId))
				{
					elog(WARNING, "will not overwrite a used ItemId");
					return InvalidOffsetNumber;
				}
			}
		}
		else//覆盖原有数据
		{
            //指定的行偏移不在空闲空间中,需要移动原数据为新数据腾位置
			if (offsetNumber < limit)
				needshuffle = true; /* need to move existing linp's */
		}
	}
	else//没有指定数据存储行偏移
	{
		/* offsetNumber was not passed in, so find a free slot */
		/* if no free slot, we'll put it at limit (1st open slot) */
		if (PageHasFreeLinePointers(phdr))//页头编辑提示存在已回收的空间
		{
			/*
			 * Look for "recyclable" (unused) ItemId.  We check for no storage
			 * as well, just to be paranoid --- unused items should never have
			 * storage.
			 */
            //循环找出第1个可用的空闲行偏移
			for (offsetNumber = 1; offsetNumber < limit; offsetNumber++)
			{
				itemId = PageGetItemId(phdr, offsetNumber);
				if (!ItemIdIsUsed(itemId) && !ItemIdHasStorage(itemId))
					break;
			}
            //没有找到,说明页头标记有误,需要清除标记,以免误导
			if (offsetNumber >= limit)
			{
				/* the hint is wrong, so reset it */
				PageClearHasFreeLinePointers(phdr);
			}
		}
		else//没有已回收的空间,行指针/数据存储到Free space中
		{
			/* don't bother searching if hint says there's no free slot */
			offsetNumber = limit;
		}
	}

	/* Reject placing items beyond the first unused line pointer */
	if (offsetNumber > limit)
	{
        //如果指定的偏移大于空闲空间可用的第1个位置,报错
		elog(WARNING, "specified item offset is too large");
		return InvalidOffsetNumber;
	}

	/* Reject placing items beyond heap boundary, if heap */
	if ((flags & PAI_IS_HEAP) != 0 && offsetNumber > MaxHeapTuplesPerPage)
	{
        //Heap数据,但偏移大于一页可存储的最大Tuple数,报错
		elog(WARNING, "can't put more than MaxHeapTuplesPerPage items in a heap page");
		return InvalidOffsetNumber;
	}

	/*
	 * Compute new lower and upper pointers for page, see if it'll fit.
	 *
	 * Note: do arithmetic as signed ints, to avoid mistakes if, say,
	 * alignedSize > pd_upper.
	 */
	if (offsetNumber == limit || needshuffle)
        //如果数据存储在Free space中,修改lower值
		lower = phdr->pd_lower + sizeof(ItemIdData);
	else
		lower = phdr->pd_lower;//否则,找到了已回收的空闲位置,使用原有的lower

	alignedSize = MAXALIGN(size);//大小对齐

	upper = (int) phdr->pd_upper - (int) alignedSize;//申请存储空间

	if (lower > upper)//校验Free space低位和Free space高位
		return InvalidOffsetNumber;

	/*
	 * OK to insert the item.  First, shuffle the existing pointers if needed.
	 */
    //获取行指针
	itemId = PageGetItemId(phdr, offsetNumber);

	if (needshuffle)
        //如果需要腾位置,把原有的行指针往后挪一”格“
		memmove(itemId + 1, itemId,
				(limit - offsetNumber) * sizeof(ItemIdData));

	/* set the line pointer */
    //设置新数据行指针
	ItemIdSetNormal(itemId, upper, size);

	/*
	 * Items normally contain no uninitialized bytes.  Core bufpage consumers
	 * conform, but this is not a necessary coding rule; a new index AM could
	 * opt to depart from it.  However, data type input functions and other
	 * C-language functions that synthesize datums should initialize all
	 * bytes; datumIsEqual() relies on this.  Testing here, along with the
	 * similar check in printtup(), helps to catch such mistakes.
	 *
	 * Values of the "name" type retrieved via index-only scans may contain
	 * uninitialized bytes; see comment in btrescan().  Valgrind will report
	 * this as an error, but it is safe to ignore.
	 */
	VALGRIND_CHECK_MEM_IS_DEFINED(item, size);

	/* copy the item's data onto the page */
    //把数据放在数据区
	memcpy((char *) page + upper, item, size);

	/* adjust page header */
    //更新页头的lower & upper
	phdr->pd_lower = (LocationIndex) lower;
	phdr->pd_upper = (LocationIndex) upper;

    //返回实际的行偏移
	return offsetNumber;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值