⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rtree.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 3 页
字号:
	if (BufferGetBlockNumber(buffer) == P_ROOT)	{		leftbuf = ReadBuffer(r, P_NEW);		RTInitBuffer(leftbuf, opaque->flags);		lbknum = BufferGetBlockNumber(leftbuf);		left = (Page) BufferGetPage(leftbuf);	}	else	{		leftbuf = buffer;		IncrBufferRefCount(buffer);		lbknum = BufferGetBlockNumber(buffer);		left = (Page) PageGetTempPage(p, sizeof(RTreePageOpaqueData));	}	rightbuf = ReadBuffer(r, P_NEW);	RTInitBuffer(rightbuf, opaque->flags);	rbknum = BufferGetBlockNumber(rightbuf);	right = (Page) BufferGetPage(rightbuf);	spl_left = v.spl_left;	spl_right = v.spl_right;	leftoff = rightoff = FirstOffsetNumber;	maxoff = PageGetMaxOffsetNumber(p);	newitemoff = OffsetNumberNext(maxoff);	/*	 * spl_left contains a list of the offset numbers of the tuples that will	 * go to the left page.  For each offset number, get the tuple item, then	 * add the item to the left page.  Similarly for the right side.	 */	/* fill left node */	for (n = 0; n < v.spl_nleft; n++)	{		i = *spl_left;		if (i == newitemoff)			item = itup;		else		{			itemid = PageGetItemId(p, i);			item = (IndexTuple) PageGetItem(p, itemid);		}		if (PageAddItem(left, (Item) item, IndexTupleSize(item),						leftoff, LP_USED) == InvalidOffsetNumber)			elog(ERROR, "failed to add index item to \"%s\"",				 RelationGetRelationName(r));		leftoff = OffsetNumberNext(leftoff);		spl_left++;				/* advance in left split vector */	}	/* fill right node */	for (n = 0; n < v.spl_nright; n++)	{		i = *spl_right;		if (i == newitemoff)			item = itup;		else		{			itemid = PageGetItemId(p, i);			item = (IndexTuple) PageGetItem(p, itemid);		}		if (PageAddItem(right, (Item) item, IndexTupleSize(item),						rightoff, LP_USED) == InvalidOffsetNumber)			elog(ERROR, "failed to add index item to \"%s\"",				 RelationGetRelationName(r));		rightoff = OffsetNumberNext(rightoff);		spl_right++;			/* advance in right split vector */	}	/* Make sure we consumed all of the split vectors, and release 'em */	Assert(*spl_left == InvalidOffsetNumber);	Assert(*spl_right == InvalidOffsetNumber);	pfree(v.spl_left);	pfree(v.spl_right);	if ((bufblock = BufferGetBlockNumber(buffer)) != P_ROOT)		PageRestoreTempPage(left, p);	WriteBuffer(leftbuf);	WriteBuffer(rightbuf);	/*	 * Okay, the page is split.  We have three things left to do:	 *	 * 1)  Adjust any active scans on this index to cope with changes we	 * introduced in its structure by splitting this page.	 *	 * 2)  "Tighten" the bounding box of the pointer to the left page in the	 * parent node in the tree, if any.  Since we moved a bunch of stuff off	 * the left page, we expect it to get smaller.	This happens in the	 * internal insertion routine.	 *	 * 3)  Insert a pointer to the right page in the parent.  This may cause	 * the parent to split.  If it does, we need to repeat steps one and two	 * for each split node in the tree.	 */	/* adjust active scans */	rtadjscans(r, RTOP_SPLIT, bufblock, FirstOffsetNumber);	tupDesc = r->rd_att;	isnull = (bool *) palloc(r->rd_rel->relnatts * sizeof(bool));	memset(isnull, false, r->rd_rel->relnatts * sizeof(bool));	ltup = index_form_tuple(tupDesc, &(v.spl_ldatum), isnull);	rtup = index_form_tuple(tupDesc, &(v.spl_rdatum), isnull);	pfree(isnull);	pfree(DatumGetPointer(v.spl_ldatum));	pfree(DatumGetPointer(v.spl_rdatum));	/* set pointers to new child pages in the internal index tuples */	ItemPointerSet(&(ltup->t_tid), lbknum, 1);	ItemPointerSet(&(rtup->t_tid), rbknum, 1);	rtintinsert(r, stack, ltup, rtup, rtstate);	pfree(ltup);	pfree(rtup);}static voidrtintinsert(Relation r,			RTSTACK *stk,			IndexTuple ltup,			IndexTuple rtup,			RTSTATE *rtstate){	IndexTuple	old;	Buffer		b;	Page		p;	Datum		ldatum,				rdatum,				newdatum;	if (stk == NULL)	{		rtnewroot(r, ltup, rtup);		return;	}	b = ReadBuffer(r, stk->rts_blk);	p = BufferGetPage(b);	old = (IndexTuple) PageGetItem(p, PageGetItemId(p, stk->rts_child));	/*	 * This is a hack.	Right now, we force rtree internal keys to be constant	 * size. To fix this, need delete the old key and add both left and right	 * for the two new pages.  The insertion of left may force a split if the	 * new left key is bigger than the old key.	 */	if (IndexTupleSize(old) != IndexTupleSize(ltup))		ereport(ERROR,				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),				 errmsg("variable-length rtree keys are not supported")));	/* install pointer to left child */	memmove(old, ltup, IndexTupleSize(ltup));	if (nospace(p, rtup))	{		newdatum = IndexTupleGetDatum(ltup);		rttighten(r, stk->rts_parent, newdatum,				  IndexTupleAttSize(ltup), rtstate);		rtdosplit(r, b, stk->rts_parent, rtup, rtstate);		WriteBuffer(b);			/* don't forget to release buffer!  - 01/31/94 */	}	else	{		if (PageAddItem(p, (Item) rtup, IndexTupleSize(rtup),						PageGetMaxOffsetNumber(p),						LP_USED) == InvalidOffsetNumber)			elog(ERROR, "failed to add index item to \"%s\"",				 RelationGetRelationName(r));		WriteBuffer(b);		ldatum = IndexTupleGetDatum(ltup);		rdatum = IndexTupleGetDatum(rtup);		newdatum = FunctionCall2(&rtstate->unionFn, ldatum, rdatum);		rttighten(r, stk->rts_parent, newdatum,				  IndexTupleAttSize(rtup), rtstate);		pfree(DatumGetPointer(newdatum));	}}static voidrtnewroot(Relation r, IndexTuple lt, IndexTuple rt){	Buffer		b;	Page		p;	b = ReadBuffer(r, P_ROOT);	RTInitBuffer(b, 0);	p = BufferGetPage(b);	if (PageAddItem(p, (Item) lt, IndexTupleSize(lt),					FirstOffsetNumber,					LP_USED) == InvalidOffsetNumber)		elog(ERROR, "failed to add index item to \"%s\"",			 RelationGetRelationName(r));	if (PageAddItem(p, (Item) rt, IndexTupleSize(rt),					OffsetNumberNext(FirstOffsetNumber),					LP_USED) == InvalidOffsetNumber)		elog(ERROR, "failed to add index item to \"%s\"",			 RelationGetRelationName(r));	WriteBuffer(b);}/* * Choose how to split an rtree page into two pages. * * We return two vectors of index item numbers, one for the items to be * put on the left page, one for the items to be put on the right page. * In addition, the item to be added (itup) is listed in the appropriate * vector.	It is represented by item number N+1 (N = # of items on page). * * Both vectors have a terminating sentinel value of InvalidOffsetNumber, * but the sentinal value is no longer used, because the SPLITVEC * vector also contains the length of each vector, and that information * is now used to iterate over them in rtdosplit(). --kbb, 21 Sept 2001 * * The bounding-box datums for the two new pages are also returned in *v. * * This is the quadratic-cost split algorithm Guttman describes in * his paper.  The reason we chose it is that you can implement this * with less information about the data types on which you're operating. * * We must also deal with a consideration not found in Guttman's algorithm: * variable-length data.  In particular, the incoming item might be * large enough that not just any split will work.	In the worst case, * our "split" may have to be the new item on one page and all the existing * items on the other.	Short of that, we have to take care that we do not * make a split that leaves both pages too full for the new item. */static voidrtpicksplit(Relation r,			Page page,			SPLITVEC *v,			IndexTuple itup,			RTSTATE *rtstate){	OffsetNumber maxoff,				newitemoff;	OffsetNumber i,				j;	IndexTuple	item_1,				item_2;	Datum		datum_alpha,				datum_beta;	Datum		datum_l,				datum_r;	Datum		union_d,				union_dl,				union_dr;	Datum		inter_d;	bool		firsttime;	float		size_alpha,				size_beta,				size_union,				size_inter;	float		size_waste,				waste;	float		size_l,				size_r;	int			nbytes;	OffsetNumber seed_1 = 0,				seed_2 = 0;	OffsetNumber *left,			   *right;	Size		newitemsz,				item_1_sz,				item_2_sz,				left_avail_space,				right_avail_space;	int			total_num_tuples,				num_tuples_without_seeds,				max_after_split;	/* in Guttman's lingo, (M - m) */	float		diff;			/* diff between cost of putting tuple left or								 * right */	SPLITCOST  *cost_vector;	int			n;	/*	 * First, make sure the new item is not so large that we can't possibly	 * fit it on a page, even by itself.  (It's sufficient to make this test	 * here, since any oversize tuple must lead to a page split attempt.)	 */	newitemsz = IndexTupleTotalSize(itup);	if (newitemsz > RTPageAvailSpace)		ereport(ERROR,				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),				 errmsg("index row size %lu exceeds rtree maximum, %lu",						(unsigned long) newitemsz,						(unsigned long) RTPageAvailSpace),			errhint("Values larger than a buffer page cannot be indexed.")));	maxoff = PageGetMaxOffsetNumber(page);	newitemoff = OffsetNumberNext(maxoff);		/* phony index for new item */	total_num_tuples = newitemoff;	num_tuples_without_seeds = total_num_tuples - 2;	max_after_split = total_num_tuples / 2;		/* works for m = M/2 */	/* Make arrays big enough for worst case, including sentinel */	nbytes = (maxoff + 2) * sizeof(OffsetNumber);	v->spl_left = (OffsetNumber *) palloc(nbytes);	v->spl_right = (OffsetNumber *) palloc(nbytes);	firsttime = true;	waste = 0.0;	for (i = FirstOffsetNumber; i < maxoff; i = OffsetNumberNext(i))	{		item_1 = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));		datum_alpha = IndexTupleGetDatum(item_1);		item_1_sz = IndexTupleTotalSize(item_1);		for (j = OffsetNumberNext(i); j <= maxoff; j = OffsetNumberNext(j))		{			item_2 = (IndexTuple) PageGetItem(page, PageGetItemId(page, j));			datum_beta = IndexTupleGetDatum(item_2);			item_2_sz = IndexTupleTotalSize(item_2);			/*			 * Ignore seed pairs that don't leave room for the new item on			 * either split page.			 */			if (newitemsz + item_1_sz > RTPageAvailSpace &&				newitemsz + item_2_sz > RTPageAvailSpace)				continue;			/* compute the wasted space by unioning these guys */			union_d = FunctionCall2(&rtstate->unionFn,									datum_alpha, datum_beta);			FunctionCall2(&rtstate->sizeFn, union_d,						  PointerGetDatum(&size_union));			inter_d = FunctionCall2(&rtstate->interFn,									datum_alpha, datum_beta);			/*			 * The interFn may return a NULL pointer (not an SQL null!) to			 * indicate no intersection.  sizeFn must cope with this.			 */			FunctionCall2(&rtstate->sizeFn, inter_d,						  PointerGetDatum(&size_inter));			size_waste = size_union - size_inter;			if (DatumGetPointer(union_d) != NULL)				pfree(DatumGetPointer(union_d));			if (DatumGetPointer(inter_d) != NULL)				pfree(DatumGetPointer(inter_d));			/*			 * are these a more promising split that what we've already seen?			 */			if (size_waste > waste || firsttime)			{				waste = size_waste;				seed_1 = i;				seed_2 = j;				firsttime = false;			}		}	}	if (firsttime)	{		/*		 * There is no possible split except to put the new item on its own		 * page.  Since we still have to compute the union rectangles, we play		 * dumb and run through the split algorithm anyway, setting seed_1 =		 * first item on page and seed_2 = new item.		 */		seed_1 = FirstOffsetNumber;		seed_2 = newitemoff;	}	item_1 = (IndexTuple) PageGetItem(page, PageGetItemId(page, seed_1));	datum_alpha = IndexTupleGetDatum(item_1);	datum_l = FunctionCall2(&rtstate->unionFn, datum_alpha, datum_alpha);	FunctionCall2(&rtstate->sizeFn, datum_l, PointerGetDatum(&size_l));	left_avail_space = RTPageAvailSpace - IndexTupleTotalSize(item_1);	if (seed_2 == newitemoff)	{		item_2 = itup;		/* Needn't leave room for new item in calculations below */		newitemsz = 0;	}	else		item_2 = (IndexTuple) PageGetItem(page, PageGetItemId(page, seed_2));	datum_beta = IndexTupleGetDatum(item_2);	datum_r = FunctionCall2(&rtstate->unionFn, datum_beta, datum_beta);	FunctionCall2(&rtstate->sizeFn, datum_r, PointerGetDatum(&size_r));	right_avail_space = RTPageAvailSpace - IndexTupleTotalSize(item_2);	/*	 * Now split up the regions between the two seeds.	 *	 * The cost_vector array will contain hints for determining where each	 * tuple should go.  Each record in the array will contain a boolean,	 * choose_left, that indicates which node the tuple prefers to be on, and	 * the absolute difference in cost between putting the tuple in its	 * favored node and in the other node.	 *	 * Later, we will sort the cost_vector in descending order by cost	 * difference, and consider the tuples in that order for placement. That	 * way, the tuples that *really* want to be in one node or the other get	 * to choose first, and the tuples that don't really care choose last.	 *	 * First, build the cost_vector array.	The new index tuple will also be	 * handled in this loop, and represented in the array, with i==newitemoff.	 *	 * In the case of variable size tuples it is possible that we only have	 * the two seeds and no other tuples, in which case we don't do any of	 * this cost_vector stuff.	 */	/* to keep compiler quiet */	cost_vector = NULL;	if (num_tuples_without_seeds > 0)	{		cost_vector =			(SPLITCOST *) palloc(num_tuples_without_seeds * sizeof(SPLITCOST));		n = 0;		for (i = FirstOffsetNumber; i <= newitemoff; i = OffsetNumberNext(i))		{			/* Compute new union datums and sizes for both choices */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -