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

📄 nodehashjoin.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
/*------------------------------------------------------------------------- * * nodeHashjoin.c *	  Routines to handle hash join nodes * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.75.2.3 2005/11/28 23:46:24 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "executor/executor.h"#include "executor/hashjoin.h"#include "executor/nodeHash.h"#include "executor/nodeHashjoin.h"#include "optimizer/clauses.h"#include "utils/memutils.h"static TupleTableSlot *ExecHashJoinOuterGetTuple(PlanState *outerNode,						  HashJoinState *hjstate,						  uint32 *hashvalue);static TupleTableSlot *ExecHashJoinGetSavedTuple(HashJoinState *hjstate,						  BufFile *file,						  uint32 *hashvalue,						  TupleTableSlot *tupleSlot);static int	ExecHashJoinNewBatch(HashJoinState *hjstate);/* ---------------------------------------------------------------- *		ExecHashJoin * *		This function implements the Hybrid Hashjoin algorithm. * *		Note: the relation we build hash table on is the "inner" *			  the other one is "outer". * ---------------------------------------------------------------- */TupleTableSlot *				/* return: a tuple or NULL */ExecHashJoin(HashJoinState *node){	EState	   *estate;	PlanState  *outerNode;	HashState  *hashNode;	List	   *joinqual;	List	   *otherqual;	TupleTableSlot *inntuple;	ExprContext *econtext;	ExprDoneCond isDone;	HashJoinTable hashtable;	HeapTuple	curtuple;	TupleTableSlot *outerTupleSlot;	uint32		hashvalue;	int			batchno;	/*	 * get information from HashJoin node	 */	estate = node->js.ps.state;	joinqual = node->js.joinqual;	otherqual = node->js.ps.qual;	hashNode = (HashState *) innerPlanState(node);	outerNode = outerPlanState(node);	/*	 * get information from HashJoin state	 */	hashtable = node->hj_HashTable;	econtext = node->js.ps.ps_ExprContext;	/*	 * Check to see if we're still projecting out tuples from a previous join	 * tuple (because there is a function-returning-set in the projection	 * expressions).  If so, try to project another one.	 */	if (node->js.ps.ps_TupFromTlist)	{		TupleTableSlot *result;		result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);		if (isDone == ExprMultipleResult)			return result;		/* Done with that source tuple... */		node->js.ps.ps_TupFromTlist = false;	}	/*	 * If we're doing an IN join, we want to return at most one row per outer	 * tuple; so we can stop scanning the inner scan if we matched on the	 * previous try.	 */	if (node->js.jointype == JOIN_IN && node->hj_MatchedOuter)		node->hj_NeedNewOuter = true;	/*	 * Reset per-tuple memory context to free any expression evaluation	 * storage allocated in the previous tuple cycle.  Note this can't happen	 * until we're done projecting out tuples from a join tuple.	 */	ResetExprContext(econtext);	/*	 * if this is the first call, build the hash table for inner relation	 */	if (hashtable == NULL)	{		/*		 * If the outer relation is completely empty, we can quit without		 * building the hash table.  However, for an inner join it is only a		 * win to check this when the outer relation's startup cost is less		 * than the projected cost of building the hash table.	Otherwise it's		 * best to build the hash table first and see if the inner relation is		 * empty.  (When it's an outer join, we should always make this check,		 * since we aren't going to be able to skip the join on the strength		 * of an empty inner relation anyway.)		 *		 * If we are rescanning the join, we make use of information gained		 * on the previous scan: don't bother to try the prefetch if the		 * previous scan found the outer relation nonempty.  This is not		 * 100% reliable since with new parameters the outer relation might		 * yield different results, but it's a good heuristic.		 *		 * The only way to make the check is to try to fetch a tuple from the		 * outer plan node.  If we succeed, we have to stash it away for later		 * consumption by ExecHashJoinOuterGetTuple.		 */		if (node->js.jointype == JOIN_LEFT ||			(outerNode->plan->startup_cost < hashNode->ps.plan->total_cost &&			 !node->hj_OuterNotEmpty))		{			node->hj_FirstOuterTupleSlot = ExecProcNode(outerNode);			if (TupIsNull(node->hj_FirstOuterTupleSlot))			{				node->hj_OuterNotEmpty = false;				return NULL;			}			else				node->hj_OuterNotEmpty = true;		}		else			node->hj_FirstOuterTupleSlot = NULL;		/*		 * create the hash table		 */		hashtable = ExecHashTableCreate((Hash *) hashNode->ps.plan,										node->hj_HashOperators);		node->hj_HashTable = hashtable;		/*		 * execute the Hash node, to build the hash table		 */		hashNode->hashtable = hashtable;		(void) MultiExecProcNode((PlanState *) hashNode);		/*		 * If the inner relation is completely empty, and we're not doing an		 * outer join, we can quit without scanning the outer relation.		 */		if (hashtable->totalTuples == 0 && node->js.jointype != JOIN_LEFT)			return NULL;		/*		 * need to remember whether nbatch has increased since we began		 * scanning the outer relation		 */		hashtable->nbatch_outstart = hashtable->nbatch;		/*		 * Reset OuterNotEmpty for scan.  (It's OK if we fetched a tuple		 * above, because ExecHashJoinOuterGetTuple will immediately		 * set it again.)		 */		node->hj_OuterNotEmpty = false;	}	/*	 * run the hash join process	 */	for (;;)	{		/*		 * If we don't have an outer tuple, get the next one		 */		if (node->hj_NeedNewOuter)		{			outerTupleSlot = ExecHashJoinOuterGetTuple(outerNode,													   node,													   &hashvalue);			if (TupIsNull(outerTupleSlot))			{				/* end of join */				return NULL;			}			node->js.ps.ps_OuterTupleSlot = outerTupleSlot;			econtext->ecxt_outertuple = outerTupleSlot;			node->hj_NeedNewOuter = false;			node->hj_MatchedOuter = false;			/*			 * now we have an outer tuple, find the corresponding bucket for			 * this tuple from the hash table			 */			node->hj_CurHashValue = hashvalue;			ExecHashGetBucketAndBatch(hashtable, hashvalue,									  &node->hj_CurBucketNo, &batchno);			node->hj_CurTuple = NULL;			/*			 * Now we've got an outer tuple and the corresponding hash bucket,			 * but this tuple may not belong to the current batch.			 */			if (batchno != hashtable->curbatch)			{				/*				 * Need to postpone this outer tuple to a later batch. Save it				 * in the corresponding outer-batch file.				 */				Assert(batchno > hashtable->curbatch);				ExecHashJoinSaveTuple(ExecFetchSlotTuple(outerTupleSlot),									  hashvalue,									  &hashtable->outerBatchFile[batchno]);				node->hj_NeedNewOuter = true;				continue;		/* loop around for a new outer tuple */			}		}		/*		 * OK, scan the selected hash bucket for matches		 */		for (;;)		{			curtuple = ExecScanHashBucket(node, econtext);			if (curtuple == NULL)				break;			/* out of matches */			/*			 * we've got a match, but still need to test non-hashed quals			 */			inntuple = ExecStoreTuple(curtuple,									  node->hj_HashTupleSlot,									  InvalidBuffer,									  false);	/* don't pfree this tuple */			econtext->ecxt_innertuple = inntuple;			/* reset temp memory each time to avoid leaks from qual expr */			ResetExprContext(econtext);			/*			 * if we pass the qual, then save state for next call and have			 * ExecProject form the projection, store it in the tuple table,			 * and return the slot.			 *			 * Only the joinquals determine MatchedOuter status, but all quals			 * must pass to actually return the tuple.			 */			if (joinqual == NIL || ExecQual(joinqual, econtext, false))			{				node->hj_MatchedOuter = true;				if (otherqual == NIL || ExecQual(otherqual, econtext, false))				{					TupleTableSlot *result;					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);					if (isDone != ExprEndResult)					{						node->js.ps.ps_TupFromTlist =							(isDone == ExprMultipleResult);						return result;					}				}				/*				 * If we didn't return a tuple, may need to set NeedNewOuter				 */				if (node->js.jointype == JOIN_IN)				{					node->hj_NeedNewOuter = true;					break;		/* out of loop over hash bucket */				}			}		}		/*		 * Now the current outer tuple has run out of matches, so check		 * whether to emit a dummy outer-join tuple. If not, loop around to		 * get a new outer tuple.		 */		node->hj_NeedNewOuter = true;		if (!node->hj_MatchedOuter &&			node->js.jointype == JOIN_LEFT)		{			/*			 * We are doing an outer join and there were no join matches for			 * this outer tuple.  Generate a fake join tuple with nulls for			 * the inner tuple, and return it if it passes the non-join quals.			 */			econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;			if (ExecQual(otherqual, econtext, false))			{				/*				 * qualification was satisfied so we project and return the				 * slot containing the result tuple using ExecProject().				 */				TupleTableSlot *result;				result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);				if (isDone != ExprEndResult)				{					node->js.ps.ps_TupFromTlist =						(isDone == ExprMultipleResult);					return result;				}			}		}	}}/* ---------------------------------------------------------------- *		ExecInitHashJoin * *		Init routine for HashJoin node. * ---------------------------------------------------------------- */HashJoinState *ExecInitHashJoin(HashJoin *node, EState *estate){	HashJoinState *hjstate;	Plan	   *outerNode;	Hash	   *hashNode;	List	   *lclauses;	List	   *rclauses;	List	   *hoperators;	ListCell   *l;	/*	 * create state structure	 */	hjstate = makeNode(HashJoinState);	hjstate->js.ps.plan = (Plan *) node;	hjstate->js.ps.state = estate;	/*	 * Miscellaneous initialization	 *	 * create expression context for node	 */	ExecAssignExprContext(estate, &hjstate->js.ps);	/*	 * initialize child expressions	 */	hjstate->js.ps.targetlist = (List *)		ExecInitExpr((Expr *) node->join.plan.targetlist,					 (PlanState *) hjstate);	hjstate->js.ps.qual = (List *)		ExecInitExpr((Expr *) node->join.plan.qual,					 (PlanState *) hjstate);	hjstate->js.jointype = node->join.jointype;	hjstate->js.joinqual = (List *)		ExecInitExpr((Expr *) node->join.joinqual,					 (PlanState *) hjstate);	hjstate->hashclauses = (List *)		ExecInitExpr((Expr *) node->hashclauses,					 (PlanState *) hjstate);	/*	 * initialize child nodes	 */	outerNode = outerPlan(node);	hashNode = (Hash *) innerPlan(node);	outerPlanState(hjstate) = ExecInitNode(outerNode, estate);	innerPlanState(hjstate) = ExecInitNode((Plan *) hashNode, estate);#define HASHJOIN_NSLOTS 3	/*	 * tuple table initialization	 */	ExecInitResultTupleSlot(estate, &hjstate->js.ps);	hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate);	switch (node->join.jointype)	{		case JOIN_INNER:		case JOIN_IN:			break;		case JOIN_LEFT:			hjstate->hj_NullInnerTupleSlot =				ExecInitNullTupleSlot(estate,								 ExecGetResultType(innerPlanState(hjstate)));			break;		default:			elog(ERROR, "unrecognized join type: %d",				 (int) node->join.jointype);	}	/*	 * now for some voodoo.  our temporary tuple slot is actually the result	 * tuple slot of the Hash node (which is our inner plan).  we do this	 * because Hash nodes don't return tuples via ExecProcNode() -- instead	 * the hash join node uses ExecScanHashBucket() to get at the contents of	 * the hash table.	-cim 6/9/91	 */	{		HashState  *hashstate = (HashState *) innerPlanState(hjstate);		TupleTableSlot *slot = hashstate->ps.ps_ResultTupleSlot;		hjstate->hj_HashTupleSlot = slot;	}	/*	 * initialize tuple type and projection info	 */	ExecAssignResultTypeFromTL(&hjstate->js.ps);	ExecAssignProjectionInfo(&hjstate->js.ps);	ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot,						  ExecGetResultType(outerPlanState(hjstate)),						  false);	/*	 * initialize hash-specific info	 */	hjstate->hj_HashTable = NULL;	hjstate->hj_FirstOuterTupleSlot = NULL;	hjstate->hj_CurHashValue = 0;	hjstate->hj_CurBucketNo = 0;

⌨️ 快捷键说明

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