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

📄 nodeindexscan.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 2 页
字号:
/*------------------------------------------------------------------------- * * nodeIndexscan.c *	  Routines to support indexes and indexed scans of relations * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.84.2.1 2003/12/30 20:05:15 tgl Exp $ * *------------------------------------------------------------------------- *//* * INTERFACE ROUTINES *		ExecIndexScan			scans a relation using indices *		ExecIndexNext			using index to retrieve next tuple *		ExecInitIndexScan		creates and initializes state info. *		ExecIndexReScan			rescans the indexed relation. *		ExecEndIndexScan		releases all storage. *		ExecIndexMarkPos		marks scan position. *		ExecIndexRestrPos		restores scan position. */#include "postgres.h"#include "access/genam.h"#include "access/heapam.h"#include "executor/execdebug.h"#include "executor/nodeIndexscan.h"#include "miscadmin.h"#include "nodes/nodeFuncs.h"#include "optimizer/clauses.h"#include "parser/parsetree.h"#define NO_OP			0#define LEFT_OP			1#define RIGHT_OP		2/* * In a multiple-index plan, we must take care to return any given tuple * only once, even if it matches conditions of several index scans.  Our * preferred way to do this is to record already-returned tuples in a hash * table (using the TID as unique identifier).  However, in a very large * scan this could conceivably run out of memory.  We limit the hash table * to no more than SortMem KB; if it grows past that, we fall back to the * pre-7.4 technique: evaluate the prior-scan index quals again for each * tuple (which is space-efficient, but slow). * * When scanning backwards, we use scannum to determine when to emit the * tuple --- we have to re-emit a tuple in the same scan as it was first * encountered. * * Note: this code would break if the planner were ever to create a multiple * index plan with overall backwards direction, because the hashtable code * will emit a tuple the first time it is encountered (which would be the * highest scan in which it matches the index), but the evaluate-the-quals * code will emit a tuple in the lowest-numbered scan in which it's valid. * This could be fixed at need by making the evaluate-the-quals case more * complex.  Currently the planner will never create such a plan (since it * considers multi-index plans unordered anyway), so there's no need for * more complexity. */typedef struct{	/* tid is the hash key and so must be first! */	ItemPointerData tid;		/* TID of a tuple we've returned */	int			scannum;		/* number of scan we returned it in */} DupHashTabEntry;static TupleTableSlot *IndexNext(IndexScanState *node);static void create_duphash(IndexScanState *node);/* ---------------------------------------------------------------- *		IndexNext * *		Retrieve a tuple from the IndexScan node's currentRelation *		using the indices in the IndexScanState information. * *		note: the old code mentions 'Primary indices'.	to my knowledge *		we only support a single secondary index. -cim 9/11/89 * * old comments: *		retrieve a tuple from relation using the indices given. *		The indices are used in the order they appear in 'indices'. *		The indices may be primary or secondary indices: *		  * primary index --	scan the relation 'relID' using keys supplied. *		  * secondary index --	scan the index relation to get the 'tid' for *								a tuple in the relation 'relID'. *		If the current index(pointed by 'indexPtr') fails to return a *		tuple, the next index in the indices is used. * *		  bug fix so that it should retrieve on a null scan key. * ---------------------------------------------------------------- */static TupleTableSlot *IndexNext(IndexScanState *node){	EState	   *estate;	ExprContext *econtext;	ScanDirection direction;	IndexScanDescPtr scanDescs;	IndexScanDesc scandesc;	Index		scanrelid;	HeapTuple	tuple;	TupleTableSlot *slot;	int			numIndices;	bool		bBackward;	int			indexNumber;	/*	 * extract necessary information from index scan node	 */	estate = node->ss.ps.state;	direction = estate->es_direction;	if (ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indxorderdir))	{		if (ScanDirectionIsForward(direction))			direction = BackwardScanDirection;		else if (ScanDirectionIsBackward(direction))			direction = ForwardScanDirection;	}	scanDescs = node->iss_ScanDescs;	numIndices = node->iss_NumIndices;	econtext = node->ss.ps.ps_ExprContext;	slot = node->ss.ss_ScanTupleSlot;	scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid;	/*	 * Check if we are evaluating PlanQual for tuple of this relation.	 * Additional checking is not good, but no other way for now. We could	 * introduce new nodes for this case and handle IndexScan --> NewNode	 * switching in Init/ReScan plan...	 */	if (estate->es_evTuple != NULL &&		estate->es_evTuple[scanrelid - 1] != NULL)	{		List	   *qual;		ExecClearTuple(slot);		if (estate->es_evTupleNull[scanrelid - 1])			return slot;		/* return empty slot */		ExecStoreTuple(estate->es_evTuple[scanrelid - 1],					   slot, InvalidBuffer, false);		/* Does the tuple meet any of the OR'd indxqual conditions? */		econtext->ecxt_scantuple = slot;		ResetExprContext(econtext);		foreach(qual, node->indxqualorig)		{			if (ExecQual((List *) lfirst(qual), econtext, false))				break;		}		if (qual == NIL)		/* would not be returned by indices */			slot->val = NULL;		/* Flag for the next call that no more tuples */		estate->es_evTupleNull[scanrelid - 1] = true;		return slot;	}	/*	 * ok, now that we have what we need, fetch an index tuple. if	 * scanning this index succeeded then return the appropriate heap	 * tuple.. else return NULL.	 */	bBackward = ScanDirectionIsBackward(direction);	if (bBackward)	{		indexNumber = numIndices - node->iss_IndexPtr - 1;		if (indexNumber < 0)		{			indexNumber = 0;			node->iss_IndexPtr = numIndices - 1;		}	}	else	{		if ((indexNumber = node->iss_IndexPtr) < 0)		{			indexNumber = 0;			node->iss_IndexPtr = 0;		}	}	while (indexNumber < numIndices)	{		scandesc = scanDescs[node->iss_IndexPtr];		while ((tuple = index_getnext(scandesc, direction)) != NULL)		{			/*			 * Store the scanned tuple in the scan tuple slot of the scan			 * state.  Note: we pass 'false' because tuples returned by			 * amgetnext are pointers onto disk pages and must not be			 * pfree()'d.			 */			ExecStoreTuple(tuple,		/* tuple to store */						   slot,	/* slot to store in */						   scandesc->xs_cbuf,	/* buffer containing tuple */						   false);		/* don't pfree */			/*			 * If it's a multiple-index scan, make sure not to double-report			 * a tuple matched by more than one index.  (See notes above.)			 */			if (numIndices > 1)			{				/* First try the hash table */				if (node->iss_DupHash)				{					DupHashTabEntry *entry;					bool	found;					entry = (DupHashTabEntry *)						hash_search(node->iss_DupHash,									&tuple->t_data->t_ctid,									HASH_ENTER,									&found);					if (entry == NULL ||						node->iss_DupHash->hctl->nentries > node->iss_MaxHash)					{						/* out of memory (either hard or soft limit) */						/* release hash table and fall thru to old code */						hash_destroy(node->iss_DupHash);						node->iss_DupHash = NULL;					}					else if (found)					{						/* pre-existing entry */						/*						 * It's duplicate if first emitted in a different						 * scan.  If same scan, we must be backing up, so						 * okay to emit again.						 */						if (entry->scannum != node->iss_IndexPtr)						{							/* Dup, so drop it and loop back for another */							ExecClearTuple(slot);							continue;						}					}					else					{						/* new entry, finish filling it in */						entry->scannum = node->iss_IndexPtr;					}				}				/* If hash table has overflowed, do it the hard way */				if (node->iss_DupHash == NULL &&					node->iss_IndexPtr > 0)				{					bool		prev_matches = false;					int			prev_index;					List	   *qual;					econtext->ecxt_scantuple = slot;					ResetExprContext(econtext);					qual = node->indxqualorig;					for (prev_index = 0;						 prev_index < node->iss_IndexPtr;						 prev_index++)					{						if (ExecQual((List *) lfirst(qual), econtext, false))						{							prev_matches = true;							break;						}						qual = lnext(qual);					}					if (prev_matches)					{						/* Dup, so drop it and loop back for another */						ExecClearTuple(slot);						continue;					}				}			}			return slot;		/* OK to return tuple */		}		if (indexNumber < numIndices)		{			indexNumber++;			if (bBackward)				node->iss_IndexPtr--;			else				node->iss_IndexPtr++;		}	}	/*	 * if we get here it means the index scan failed so we are at the end	 * of the scan..	 */	return ExecClearTuple(slot);}/* ---------------------------------------------------------------- *		ExecIndexScan(node) * * old comments: *		Scans the relation using primary or secondary indices and returns *		   the next qualifying tuple in the direction specified. *		It calls ExecScan() and passes it the access methods which returns *		the next tuple using the indices. * *		Conditions: *		  -- the "cursor" maintained by the AMI is positioned at the tuple *			 returned previously. * *		Initial States: *		  -- the relation indicated is opened for scanning so that the *			 "cursor" is positioned before the first qualifying tuple. *		  -- all index realtions are opened for scanning. *		  -- indexPtr points to the first index. *		  -- state variable ruleFlag = nil. * ---------------------------------------------------------------- */TupleTableSlot *ExecIndexScan(IndexScanState *node){	/*	 * If we have runtime keys and they've not already been set up, do it	 * now.	 */	if (node->iss_RuntimeKeyInfo && !node->iss_RuntimeKeysReady)		ExecReScan((PlanState *) node, NULL);	/*	 * use IndexNext as access method	 */	return ExecScan(&node->ss, (ExecScanAccessMtd) IndexNext);}/* ---------------------------------------------------------------- *		ExecIndexReScan(node) * *		Recalculates the value of the scan keys whose value depends on *		information known at runtime and rescans the indexed relation. *		Updating the scan key was formerly done separately in *		ExecUpdateIndexScanKeys. Integrating it into ReScan makes *		rescans of indices and relations/general streams more uniform. * * ---------------------------------------------------------------- */voidExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt){	EState	   *estate;	ExprContext *econtext;	int			numIndices;	IndexScanDescPtr scanDescs;	ScanKey    *scanKeys;	ExprState ***runtimeKeyInfo;	int		   *numScanKeys;	Index		scanrelid;	int			i;	int			j;	estate = node->ss.ps.state;	econtext = node->iss_RuntimeContext;		/* context for runtime												 * keys */	numIndices = node->iss_NumIndices;	scanDescs = node->iss_ScanDescs;	scanKeys = node->iss_ScanKeys;	runtimeKeyInfo = node->iss_RuntimeKeyInfo;	numScanKeys = node->iss_NumScanKeys;	scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid;	if (econtext)	{		/*		 * If we are being passed an outer tuple, save it for runtime key		 * calc.  We also need to link it into the "regular" per-tuple		 * econtext, so it can be used during indexqualorig evaluations.		 */		if (exprCtxt != NULL)		{			ExprContext *stdecontext;			econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple;			stdecontext = node->ss.ps.ps_ExprContext;			stdecontext->ecxt_outertuple = exprCtxt->ecxt_outertuple;		}		/*		 * Reset the runtime-key context so we don't leak memory as each		 * outer tuple is scanned.	Note this assumes that we will		 * recalculate *all* runtime keys on each call.		 */		ResetExprContext(econtext);	}	/*	 * If we are doing runtime key calculations (ie, the index keys depend	 * on data from an outer scan), compute the new key values	 */	if (runtimeKeyInfo)	{		for (i = 0; i < numIndices; i++)		{			int			n_keys;			ScanKey		scan_keys;			ExprState **run_keys;			n_keys = numScanKeys[i];			scan_keys = scanKeys[i];			run_keys = runtimeKeyInfo[i];			for (j = 0; j < n_keys; j++)			{				/*				 * If we have a run-time key, then extract the run-time				 * expression and evaluate it with respect to the current				 * outer tuple.  We then stick the result into the scan				 * key.				 *				 * Note: the result of the eval could be a pass-by-ref value				 * that's stored in the outer scan's tuple, not in				 * econtext->ecxt_per_tuple_memory.  We assume that the				 * outer tuple will stay put throughout our scan.  If this				 * is wrong, we could copy the result into our context				 * explicitly, but I think that's not necessary...				 */				if (run_keys[j] != NULL)				{					Datum		scanvalue;					bool		isNull;					scanvalue = ExecEvalExprSwitchContext(run_keys[j],														  econtext,														  &isNull,														  NULL);					scan_keys[j].sk_argument = scanvalue;					if (isNull)						scan_keys[j].sk_flags |= SK_ISNULL;					else						scan_keys[j].sk_flags &= ~SK_ISNULL;				}			}		}		node->iss_RuntimeKeysReady = true;	}	/* If this is re-scanning of PlanQual ... */	if (estate->es_evTuple != NULL &&		estate->es_evTuple[scanrelid - 1] != NULL)	{		estate->es_evTupleNull[scanrelid - 1] = false;		return;	}	/* reset hash table */	if (numIndices > 1)	{		if (node->iss_DupHash)			hash_destroy(node->iss_DupHash);		create_duphash(node);	}	/* reset index scans */	if (ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indxorderdir))		node->iss_IndexPtr = numIndices;	else		node->iss_IndexPtr = -1;	for (i = 0; i < numIndices; i++)	{		IndexScanDesc scan = scanDescs[i];		ScanKey		skey = scanKeys[i];		index_rescan(scan, skey);	}}/* ---------------------------------------------------------------- *		ExecEndIndexScan * ---------------------------------------------------------------- */voidExecEndIndexScan(IndexScanState *node){	int			numIndices;	RelationPtr indexRelationDescs;	IndexScanDescPtr indexScanDescs;	Relation	relation;	int			i;	/*	 * extract information from the node	 */	numIndices = node->iss_NumIndices;	indexRelationDescs = node->iss_RelationDescs;	indexScanDescs = node->iss_ScanDescs;	relation = node->ss.ss_currentRelation;	/*	 * Free the exprcontext(s)	 */	ExecFreeExprContext(&node->ss.ps);	if (node->iss_RuntimeContext)		FreeExprContext(node->iss_RuntimeContext);	/*	 * clear out tuple table slots	 */	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);	ExecClearTuple(node->ss.ss_ScanTupleSlot);	/* drop hash table */	if (node->iss_DupHash)		hash_destroy(node->iss_DupHash);	/*	 * close the index relations	 */	for (i = 0; i < numIndices; i++)

⌨️ 快捷键说明

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