📄 nodehashjoin.c
字号:
/* * 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_hashdone = false; hjstate->hj_HashTable = (HashJoinTable) NULL; hjstate->hj_CurBucketNo = 0; hjstate->hj_CurTuple = (HashJoinTuple) NULL; /* * The planner already made a list of the inner hashkeys for us, but * we also need a list of the outer hashkeys, as well as a list of the * hash operator OIDs. Both lists of exprs must then be prepared for * execution. */ hjstate->hj_InnerHashKeys = (List *) ExecInitExpr((Expr *) hashNode->hashkeys, (PlanState *) hjstate); ((HashState *) innerPlanState(hjstate))->hashkeys = hjstate->hj_InnerHashKeys; hclauses = NIL; hoperators = NIL; foreach(hcl, node->hashclauses) { OpExpr *hclause = (OpExpr *) lfirst(hcl); Assert(IsA(hclause, OpExpr)); hclauses = lappend(hclauses, get_leftop((Expr *) hclause)); hoperators = lappendo(hoperators, hclause->opno); } hjstate->hj_OuterHashKeys = (List *) ExecInitExpr((Expr *) hclauses, (PlanState *) hjstate); hjstate->hj_HashOperators = hoperators; hjstate->js.ps.ps_OuterTupleSlot = NULL; hjstate->js.ps.ps_TupFromTlist = false; hjstate->hj_NeedNewOuter = true; hjstate->hj_MatchedOuter = false; return hjstate;}intExecCountSlotsHashJoin(HashJoin *node){ return ExecCountSlotsNode(outerPlan(node)) + ExecCountSlotsNode(innerPlan(node)) + HASHJOIN_NSLOTS;}/* ---------------------------------------------------------------- * ExecEndHashJoin * * clean up routine for HashJoin node * ---------------------------------------------------------------- */voidExecEndHashJoin(HashJoinState *node){ /* * Free hash table */ if (node->hj_HashTable) { ExecHashTableDestroy(node->hj_HashTable); node->hj_HashTable = NULL; } /* * Free the exprcontext */ ExecFreeExprContext(&node->js.ps); /* * clean out the tuple table */ ExecClearTuple(node->js.ps.ps_ResultTupleSlot); ExecClearTuple(node->hj_OuterTupleSlot); ExecClearTuple(node->hj_HashTupleSlot); /* * clean up subtrees */ ExecEndNode(outerPlanState(node)); ExecEndNode(innerPlanState(node));}/* ---------------------------------------------------------------- * ExecHashJoinOuterGetTuple * * get the next outer tuple for hashjoin: either by * executing a plan node as in the first pass, or from * the tmp files for the hashjoin batches. * ---------------------------------------------------------------- */static TupleTableSlot *ExecHashJoinOuterGetTuple(PlanState *node, HashJoinState *hjstate){ HashJoinTable hashtable = hjstate->hj_HashTable; int curbatch = hashtable->curbatch; TupleTableSlot *slot; if (curbatch == 0) { /* if it is the first pass */ slot = ExecProcNode(node); if (!TupIsNull(slot)) return slot; /* * We have just reached the end of the first pass. Try to switch * to a saved batch. */ curbatch = ExecHashJoinNewBatch(hjstate); } /* * Try to read from a temp file. Loop allows us to advance to new * batch as needed. */ while (curbatch <= hashtable->nbatch) { slot = ExecHashJoinGetSavedTuple(hjstate, hashtable->outerBatchFile[curbatch - 1], hjstate->hj_OuterTupleSlot); if (!TupIsNull(slot)) return slot; curbatch = ExecHashJoinNewBatch(hjstate); } /* Out of batches... */ return NULL;}/* ---------------------------------------------------------------- * ExecHashJoinGetSavedTuple * * read the next tuple from a tmp file * ---------------------------------------------------------------- */static TupleTableSlot *ExecHashJoinGetSavedTuple(HashJoinState *hjstate, BufFile *file, TupleTableSlot *tupleSlot){ HeapTupleData htup; size_t nread; HeapTuple heapTuple; nread = BufFileRead(file, (void *) &htup, sizeof(HeapTupleData)); if (nread == 0) return NULL; /* end of file */ if (nread != sizeof(HeapTupleData)) ereport(ERROR, (errcode_for_file_access(), errmsg("could not read from hash-join temporary file: %m"))); heapTuple = palloc(HEAPTUPLESIZE + htup.t_len); memcpy((char *) heapTuple, (char *) &htup, sizeof(HeapTupleData)); heapTuple->t_datamcxt = CurrentMemoryContext; heapTuple->t_data = (HeapTupleHeader) ((char *) heapTuple + HEAPTUPLESIZE); nread = BufFileRead(file, (void *) heapTuple->t_data, htup.t_len); if (nread != (size_t) htup.t_len) ereport(ERROR, (errcode_for_file_access(), errmsg("could not read from hash-join temporary file: %m"))); return ExecStoreTuple(heapTuple, tupleSlot, InvalidBuffer, true);}/* ---------------------------------------------------------------- * ExecHashJoinNewBatch * * switch to a new hashjoin batch * ---------------------------------------------------------------- */static intExecHashJoinNewBatch(HashJoinState *hjstate){ HashJoinTable hashtable = hjstate->hj_HashTable; int nbatch = hashtable->nbatch; int newbatch = hashtable->curbatch + 1; long *innerBatchSize = hashtable->innerBatchSize; long *outerBatchSize = hashtable->outerBatchSize; BufFile *innerFile; TupleTableSlot *slot; ExprContext *econtext; List *innerhashkeys; if (newbatch > 1) { /* * We no longer need the previous outer batch file; close it right * away to free disk space. */ BufFileClose(hashtable->outerBatchFile[newbatch - 2]); hashtable->outerBatchFile[newbatch - 2] = NULL; } /* * Normally we can skip over any batches that are empty on either side * --- but for JOIN_LEFT, can only skip when left side is empty. * Release associated temp files right away. */ while (newbatch <= nbatch && (outerBatchSize[newbatch - 1] == 0L || (innerBatchSize[newbatch - 1] == 0L && hjstate->js.jointype != JOIN_LEFT))) { BufFileClose(hashtable->innerBatchFile[newbatch - 1]); hashtable->innerBatchFile[newbatch - 1] = NULL; BufFileClose(hashtable->outerBatchFile[newbatch - 1]); hashtable->outerBatchFile[newbatch - 1] = NULL; newbatch++; } if (newbatch > nbatch) return newbatch; /* no more batches */ /* * Rewind inner and outer batch files for this batch, so that we can * start reading them. */ if (BufFileSeek(hashtable->outerBatchFile[newbatch - 1], 0, 0L, SEEK_SET)) ereport(ERROR, (errcode_for_file_access(), errmsg("could not rewind hash-join temporary file: %m"))); innerFile = hashtable->innerBatchFile[newbatch - 1]; if (BufFileSeek(innerFile, 0, 0L, SEEK_SET)) ereport(ERROR, (errcode_for_file_access(), errmsg("could not rewind hash-join temporary file: %m"))); /* * Reload the hash table with the new inner batch */ ExecHashTableReset(hashtable, innerBatchSize[newbatch - 1]); econtext = hjstate->js.ps.ps_ExprContext; innerhashkeys = hjstate->hj_InnerHashKeys; while ((slot = ExecHashJoinGetSavedTuple(hjstate, innerFile, hjstate->hj_HashTupleSlot)) && !TupIsNull(slot)) { econtext->ecxt_innertuple = slot; ExecHashTableInsert(hashtable, econtext, innerhashkeys); } /* * after we build the hash table, the inner batch file is no longer * needed */ BufFileClose(innerFile); hashtable->innerBatchFile[newbatch - 1] = NULL; hashtable->curbatch = newbatch; return newbatch;}/* ---------------------------------------------------------------- * ExecHashJoinSaveTuple * * save a tuple to a tmp file. * * The data recorded in the file for each tuple is an image of its * HeapTupleData (with meaningless t_data pointer) followed by the * HeapTupleHeader and tuple data. * ---------------------------------------------------------------- */voidExecHashJoinSaveTuple(HeapTuple heapTuple, BufFile *file){ size_t written; written = BufFileWrite(file, (void *) heapTuple, sizeof(HeapTupleData)); if (written != sizeof(HeapTupleData)) ereport(ERROR, (errcode_for_file_access(), errmsg("could not write to hash-join temporary file: %m"))); written = BufFileWrite(file, (void *) heapTuple->t_data, heapTuple->t_len); if (written != (size_t) heapTuple->t_len) ereport(ERROR, (errcode_for_file_access(), errmsg("could not write to hash-join temporary file: %m")));}voidExecReScanHashJoin(HashJoinState *node, ExprContext *exprCtxt){ /* * If we haven't yet built the hash table then we can just return; * nothing done yet, so nothing to undo. */ if (!node->hj_hashdone) return; Assert(node->hj_HashTable != NULL); /* * In a multi-batch join, we currently have to do rescans the hard * way, primarily because batch temp files may have already been * released. But if it's a single-batch join, and there is no * parameter change for the inner subnode, then we can just re-use the * existing hash table without rebuilding it. */ if (node->hj_HashTable->nbatch == 0 && ((PlanState *) node)->righttree->chgParam == NULL) { /* okay to reuse the hash table; needn't rescan inner, either */ } else { /* must destroy and rebuild hash table */ node->hj_hashdone = false; ExecHashTableDestroy(node->hj_HashTable); node->hj_HashTable = NULL; /* * if chgParam of subnode is not null then plan will be re-scanned * by first ExecProcNode. */ if (((PlanState *) node)->righttree->chgParam == NULL) ExecReScan(((PlanState *) node)->righttree, exprCtxt); } /* Always reset intra-tuple state */ node->hj_CurBucketNo = 0; node->hj_CurTuple = (HashJoinTuple) NULL; node->js.ps.ps_OuterTupleSlot = (TupleTableSlot *) NULL; node->js.ps.ps_TupFromTlist = false; node->hj_NeedNewOuter = true; node->hj_MatchedOuter = false; /* * if chgParam of subnode is not null then plan will be re-scanned by * first ExecProcNode. */ if (((PlanState *) node)->lefttree->chgParam == NULL) ExecReScan(((PlanState *) node)->lefttree, exprCtxt);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -