nbtsearch.c

来自「PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统」· C语言 代码 · 共 1,313 行 · 第 1/3 页

C
1,313
字号
	do	{		/* step one tuple in the appropriate direction */		if (!_bt_step(scan, &buf, dir))			return false;		/* current is the next candidate tuple to return */		offnum = ItemPointerGetOffsetNumber(current);		page = BufferGetPage(buf);		btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum));		itup = &btitem->bti_itup;		if (_bt_checkkeys(scan, itup, dir, &continuescan))		{			/* tuple passes all scan key conditions, so return it */			scan->xs_ctup.t_self = itup->t_tid;			return true;		}		/* This tuple doesn't pass, but there might be more that do */	} while (continuescan);	/* No more items, so close down the current-item info */	ItemPointerSetInvalid(current);	so->btso_curbuf = InvalidBuffer;	_bt_relbuf(rel, buf);	return false;}/* *	_bt_first() -- Find the first item in a scan. * *		We need to be clever about the type of scan, the operation it's *		performing, and the tree ordering.	We find the *		first item in the tree that satisfies the qualification *		associated with the scan descriptor.  On exit, the page containing *		the current index tuple is read locked and pinned, and the scan's *		opaque data entry is updated to include the buffer. */bool_bt_first(IndexScanDesc scan, ScanDirection dir){	Relation	rel = scan->indexRelation;	BTScanOpaque so = (BTScanOpaque) scan->opaque;	Buffer		buf;	Page		page;	BTStack		stack;	OffsetNumber offnum;	BTItem		btitem;	IndexTuple	itup;	ItemPointer current;	BlockNumber blkno;	StrategyNumber strat;	bool		res;	bool		nextkey;	bool		goback;	bool		continuescan;	ScanKey		startKeys[INDEX_MAX_KEYS];	ScanKeyData scankeys[INDEX_MAX_KEYS];	int			keysCount = 0;	int			i;	StrategyNumber strat_total;	pgstat_count_index_scan(&scan->xs_pgstat_info);	/*	 * Examine the scan keys and eliminate any redundant keys; also discover	 * how many keys must be matched to continue the scan.	 */	_bt_preprocess_keys(scan);	/*	 * Quit now if _bt_preprocess_keys() discovered that the scan keys can	 * never be satisfied (eg, x == 1 AND x > 2).	 */	if (!so->qual_ok)		return false;	/*----------	 * Examine the scan keys to discover where we need to start the scan.	 *	 * We want to identify the keys that can be used as starting boundaries;	 * these are =, >, or >= keys for a forward scan or =, <, <= keys for	 * a backwards scan.  We can use keys for multiple attributes so long as	 * the prior attributes had only =, >= (resp. =, <=) keys.	Once we accept	 * a > or < boundary or find an attribute with no boundary (which can be	 * thought of as the same as "> -infinity"), we can't use keys for any	 * attributes to its right, because it would break our simplistic notion	 * of what initial positioning strategy to use.	 *	 * When the scan keys include non-default operators, _bt_preprocess_keys	 * may not be able to eliminate redundant keys; in such cases we will	 * arbitrarily pick a usable one for each attribute.  This is correct	 * but possibly not optimal behavior.  (For example, with keys like	 * "x >= 4 AND x >= 5" we would elect to scan starting at x=4 when	 * x=5 would be more efficient.)  Since the situation only arises in	 * hokily-worded queries, live with it.	 *	 * When both equality and inequality keys appear for a single attribute	 * (again, only possible when non-default operators appear), we *must*	 * select one of the equality keys for the starting point, because	 * _bt_checkkeys() will stop the scan as soon as an equality qual fails.	 * For example, if we have keys like "x >= 4 AND x = 10" and we elect to	 * start at x=4, we will fail and stop before reaching x=10.  If multiple	 * equality quals survive preprocessing, however, it doesn't matter which	 * one we use --- by definition, they are either redundant or	 * contradictory.	 *----------	 */	strat_total = BTEqualStrategyNumber;	if (so->numberOfKeys > 0)	{		AttrNumber	curattr;		ScanKey		chosen;		ScanKey		cur;		/*		 * chosen is the so-far-chosen key for the current attribute, if any.		 * We don't cast the decision in stone until we reach keys for the		 * next attribute.		 */		curattr = 1;		chosen = NULL;		/*		 * Loop iterates from 0 to numberOfKeys inclusive; we use the last		 * pass to handle after-last-key processing.  Actual exit from the		 * loop is at one of the "break" statements below.		 */		for (cur = so->keyData, i = 0;; cur++, i++)		{			if (i >= so->numberOfKeys || cur->sk_attno != curattr)			{				/*				 * Done looking at keys for curattr.  If we didn't find a				 * usable boundary key, quit; else save the boundary key				 * pointer in startKeys.				 */				if (chosen == NULL)					break;				startKeys[keysCount++] = chosen;				/*				 * Adjust strat_total, and quit if we have stored a > or <				 * key.				 */				strat = chosen->sk_strategy;				if (strat != BTEqualStrategyNumber)				{					strat_total = strat;					if (strat == BTGreaterStrategyNumber ||						strat == BTLessStrategyNumber)						break;				}				/*				 * Done if that was the last attribute, or if next key is not				 * in sequence (implying no boundary key is available for the				 * next attribute).				 */				if (i >= so->numberOfKeys ||					cur->sk_attno != curattr + 1)					break;				/*				 * Reset for next attr.				 */				curattr = cur->sk_attno;				chosen = NULL;			}			/* Can we use this key as a starting boundary for this attr? */			switch (cur->sk_strategy)			{				case BTLessStrategyNumber:				case BTLessEqualStrategyNumber:					if (chosen == NULL && ScanDirectionIsBackward(dir))						chosen = cur;					break;				case BTEqualStrategyNumber:					/* override any non-equality choice */					chosen = cur;					break;				case BTGreaterEqualStrategyNumber:				case BTGreaterStrategyNumber:					if (chosen == NULL && ScanDirectionIsForward(dir))						chosen = cur;					break;			}		}	}	/*	 * If we found no usable boundary keys, we have to start from one end of	 * the tree.  Walk down that edge to the first or last key, and scan from	 * there.	 */	if (keysCount == 0)		return _bt_endpoint(scan, dir);	/*	 * We want to start the scan somewhere within the index.  Set up a	 * 3-way-comparison scankey we can use to search for the boundary point we	 * identified above.	 */	Assert(keysCount <= INDEX_MAX_KEYS);	for (i = 0; i < keysCount; i++)	{		ScanKey		cur = startKeys[i];		/*		 * _bt_preprocess_keys disallows it, but it's place to add some code		 * later		 */		if (cur->sk_flags & SK_ISNULL)			elog(ERROR, "btree doesn't support is(not)null, yet");		/*		 * If scankey operator is of default subtype, we can use the cached		 * comparison procedure; otherwise gotta look it up in the catalogs.		 */		if (cur->sk_subtype == InvalidOid)		{			FmgrInfo   *procinfo;			procinfo = index_getprocinfo(rel, i + 1, BTORDER_PROC);			ScanKeyEntryInitializeWithInfo(scankeys + i,										   cur->sk_flags,										   i + 1,										   InvalidStrategy,										   InvalidOid,										   procinfo,										   cur->sk_argument);		}		else		{			RegProcedure cmp_proc;			cmp_proc = get_opclass_proc(rel->rd_indclass->values[i],										cur->sk_subtype,										BTORDER_PROC);			ScanKeyEntryInitialize(scankeys + i,								   cur->sk_flags,								   i + 1,								   InvalidStrategy,								   cur->sk_subtype,								   cmp_proc,								   cur->sk_argument);		}	}	/*	 * Examine the selected initial-positioning strategy to determine exactly	 * where we need to start the scan, and set flag variables to control the	 * code below.	 *	 * If nextkey = false, _bt_search and _bt_binsrch will locate the first	 * item >= scan key.  If nextkey = true, they will locate the first item >	 * scan key.	 *	 * If goback = true, we will then step back one item, while if goback =	 * false, we will start the scan on the located item.	 *	 * it's yet other place to add some code later for is(not)null ...	 */	switch (strat_total)	{		case BTLessStrategyNumber:			/*			 * Find first item >= scankey, then back up one to arrive at last			 * item < scankey.	(Note: this positioning strategy is only used			 * for a backward scan, so that is always the correct starting			 * position.)			 */			nextkey = false;			goback = true;			break;		case BTLessEqualStrategyNumber:			/*			 * Find first item > scankey, then back up one to arrive at last			 * item <= scankey.  (Note: this positioning strategy is only used			 * for a backward scan, so that is always the correct starting			 * position.)			 */			nextkey = true;			goback = true;			break;		case BTEqualStrategyNumber:			/*			 * If a backward scan was specified, need to start with last equal			 * item not first one.			 */			if (ScanDirectionIsBackward(dir))			{				/*				 * This is the same as the <= strategy.  We will check at the				 * end whether the found item is actually =.				 */				nextkey = true;				goback = true;			}			else			{				/*				 * This is the same as the >= strategy.  We will check at the				 * end whether the found item is actually =.				 */				nextkey = false;				goback = false;			}			break;		case BTGreaterEqualStrategyNumber:			/*			 * Find first item >= scankey.	(This is only used for forward			 * scans.)			 */			nextkey = false;			goback = false;			break;		case BTGreaterStrategyNumber:			/*			 * Find first item > scankey.  (This is only used for forward			 * scans.)			 */			nextkey = true;			goback = false;			break;		default:			/* can't get here, but keep compiler quiet */			elog(ERROR, "unrecognized strat_total: %d", (int) strat_total);			return false;	}	/*	 * Use the manufactured scan key to descend the tree and position	 * ourselves on the target leaf page.	 */	stack = _bt_search(rel, keysCount, scankeys, nextkey, &buf, BT_READ);	/* don't need to keep the stack around... */	_bt_freestack(stack);	current = &(scan->currentItemData);	if (!BufferIsValid(buf))	{		/* Only get here if index is completely empty */		ItemPointerSetInvalid(current);		so->btso_curbuf = InvalidBuffer;		return false;	}	/* remember which buffer we have pinned */	so->btso_curbuf = buf;	/* position to the precise item on the page */	offnum = _bt_binsrch(rel, buf, keysCount, scankeys, nextkey);	page = BufferGetPage(buf);	blkno = BufferGetBlockNumber(buf);	ItemPointerSet(current, blkno, offnum);	/*	 * If nextkey = false, we are positioned at the first item >= scan key, or	 * possibly at the end of a page on which all the existing items are less	 * than the scan key and we know that everything on later pages is greater	 * than or equal to scan key.	 *	 * If nextkey = true, we are positioned at the first item > scan key, or	 * possibly at the end of a page on which all the existing items are less	 * than or equal to the scan key and we know that everything on later	 * pages is greater than scan key.	 *	 * The actually desired starting point is either this item or the prior	 * one, or in the end-of-page case it's the first item on the next page or	 * the last item on this page.	We apply _bt_step if needed to get to the	 * right place.	 *	 * If _bt_step fails (meaning we fell off the end of the index in one	 * direction or the other), then there are no matches so we just return	 * false.	 */	if (goback)	{		/* _bt_step will do the right thing if we are at end-of-page */		if (!_bt_step(scan, &buf, BackwardScanDirection))			return false;	}	else	{		/* If we're at end-of-page, must step forward to next page */		if (offnum > PageGetMaxOffsetNumber(page))		{			if (!_bt_step(scan, &buf, ForwardScanDirection))				return false;		}	}	/* okay, current item pointer for the scan is right */	offnum = ItemPointerGetOffsetNumber(current);	page = BufferGetPage(buf);	btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum));	itup = &btitem->bti_itup;	/* is the first item actually acceptable? */	if (_bt_checkkeys(scan, itup, dir, &continuescan))	{		/* yes, return it */		scan->xs_ctup.t_self = itup->t_tid;		res = true;	}	else if (continuescan)	{		/* no, but there might be another one that is */		res = _bt_next(scan, dir);	}	else	{		/* no tuples in the index match this scan key */		ItemPointerSetInvalid(current);		so->btso_curbuf = InvalidBuffer;		_bt_relbuf(rel, buf);		res = false;	}	return res;}

⌨️ 快捷键说明

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