📄 nodehashjoin.c
字号:
/*------------------------------------------------------------------------- * * nodeHashjoin.c * Routines to handle hash join nodes * * 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/nodeHashjoin.c,v 1.57.2.2 2004/09/17 18:29:10 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "executor/executor.h"#include "executor/nodeHash.h"#include "executor/nodeHashjoin.h"#include "optimizer/clauses.h"#include "utils/memutils.h"static TupleTableSlot *ExecHashJoinOuterGetTuple(PlanState *node, HashJoinState *hjstate);static TupleTableSlot *ExecHashJoinGetSavedTuple(HashJoinState *hjstate, BufFile *file, TupleTableSlot *tupleSlot);static int ExecHashJoinNewBatch(HashJoinState *hjstate);/* ---------------------------------------------------------------- * ExecHashJoin * * This function implements the Hybrid Hashjoin algorithm. * recursive partitioning remains to be added. * 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 *hjclauses; List *outerkeys; List *joinqual; List *otherqual; ScanDirection dir; TupleTableSlot *inntuple; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; HeapTuple curtuple; TupleTableSlot *outerTupleSlot; int i; /* * get information from HashJoin node */ hjclauses = node->hashclauses; estate = node->js.ps.state; joinqual = node->js.joinqual; otherqual = node->js.ps.qual; hashNode = (HashState *) innerPlanState(node); outerNode = outerPlanState(node); dir = estate->es_direction; /* * get information from HashJoin state */ hashtable = node->hj_HashTable; outerkeys = node->hj_OuterHashKeys; 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 (!node->hj_hashdone) { /* * create the hash table */ Assert(hashtable == NULL); 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) ExecProcNode((PlanState *) hashNode); /* * Open temp files for outer batches, if needed. Note that file * buffers are palloc'd in regular executor context. */ for (i = 0; i < hashtable->nbatch; i++) hashtable->outerBatchFile[i] = BufFileCreateTemp(false); node->hj_hashdone = true; } /* * Now get an outer tuple and probe into the hash table for matches */ outerTupleSlot = node->js.ps.ps_OuterTupleSlot; for (;;) { /* * If we don't have an outer tuple, get the next one */ if (node->hj_NeedNewOuter) { outerTupleSlot = ExecHashJoinOuterGetTuple(outerNode, node); 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_CurBucketNo = ExecHashGetBucket(hashtable, econtext, outerkeys); 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. * This need only be checked in the first pass. */ if (hashtable->curbatch == 0) { int batchno = ExecHashGetBatch(node->hj_CurBucketNo, hashtable); if (batchno >= 0) { /* * Need to postpone this outer tuple to a later batch. * Save it in the corresponding outer-batch file. */ hashtable->outerBatchSize[batchno]++; ExecHashJoinSaveTuple(outerTupleSlot->val, 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, hjclauses, 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 (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 *hclauses; List *hoperators; List *hcl; /* * 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); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -