nodeHashjoin.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:18k
源码类别:

数据库系统

开发平台:

Unix_Linux

  1. /*-------------------------------------------------------------------------
  2.  *
  3.  * nodeHashjoin.c
  4.  *   Routines to handle hash join nodes
  5.  *
  6.  * Copyright (c) 1994, Regents of the University of California
  7.  *
  8.  *
  9.  * IDENTIFICATION
  10.  *   $Header: /usr/local/cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.22 1999/05/25 22:41:01 momjian Exp $
  11.  *
  12.  *-------------------------------------------------------------------------
  13.  */
  14. #include <sys/types.h>
  15. #include <string.h>
  16. #include "postgres.h"
  17. #include "executor/execdebug.h"
  18. #include "executor/executor.h"
  19. #include "executor/nodeHash.h"
  20. #include "executor/nodeHashjoin.h"
  21. #include "optimizer/clauses.h" /* for get_leftop */
  22. static TupleTableSlot *ExecHashJoinOuterGetTuple(Plan *node, Plan *parent,
  23.   HashJoinState *hjstate);
  24. static TupleTableSlot *ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
  25.   BufFile *file,
  26.   TupleTableSlot *tupleSlot);
  27. static int ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable);
  28. static int ExecHashJoinNewBatch(HashJoinState *hjstate);
  29. /* ----------------------------------------------------------------
  30.  * ExecHashJoin
  31.  *
  32.  * This function implements the Hybrid Hashjoin algorithm.
  33.  * recursive partitioning remains to be added.
  34.  * Note: the relation we build hash table on is the inner
  35.  *   the other one is outer.
  36.  * ----------------------------------------------------------------
  37.  */
  38. TupleTableSlot * /* return: a tuple or NULL */
  39. ExecHashJoin(HashJoin *node)
  40. {
  41. HashJoinState *hjstate;
  42. EState    *estate;
  43. Plan    *outerNode;
  44. Hash    *hashNode;
  45. List    *hjclauses;
  46. Expr    *clause;
  47. List    *qual;
  48. ScanDirection dir;
  49. TupleTableSlot *inntuple;
  50. Var    *outerVar;
  51. ExprContext *econtext;
  52. HashJoinTable hashtable;
  53. HeapTuple curtuple;
  54. bool qualResult;
  55. TupleTableSlot *outerTupleSlot;
  56. TupleTableSlot *innerTupleSlot;
  57. Var    *innerhashkey;
  58. int i;
  59. bool hashPhaseDone;
  60. /* ----------------
  61.  * get information from HashJoin node
  62.  * ----------------
  63.  */
  64. hjstate = node->hashjoinstate;
  65. hjclauses = node->hashclauses;
  66. clause = lfirst(hjclauses);
  67. estate = node->join.state;
  68. qual = node->join.qual;
  69. hashNode = (Hash *) innerPlan(node);
  70. outerNode = outerPlan(node);
  71. hashPhaseDone = node->hashdone;
  72. dir = estate->es_direction;
  73. /* -----------------
  74.  * get information from HashJoin state
  75.  * -----------------
  76.  */
  77. hashtable = hjstate->hj_HashTable;
  78. /* --------------------
  79.  * initialize expression context
  80.  * --------------------
  81.  */
  82. econtext = hjstate->jstate.cs_ExprContext;
  83. if (hjstate->jstate.cs_TupFromTlist)
  84. {
  85. TupleTableSlot *result;
  86. bool isDone;
  87. result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
  88. if (!isDone)
  89. return result;
  90. }
  91. /* ----------------
  92.  * if this is the first call, build the hash table for inner relation
  93.  * ----------------
  94.  */
  95. if (!hashPhaseDone)
  96. { /* if the hash phase not completed */
  97. if (hashtable == NULL)
  98. { /* if the hash table has not been created */
  99. /* ----------------
  100.  * create the hash table
  101.  * ----------------
  102.  */
  103. hashtable = ExecHashTableCreate(hashNode);
  104. hjstate->hj_HashTable = hashtable;
  105. innerhashkey = hashNode->hashkey;
  106. hjstate->hj_InnerHashKey = innerhashkey;
  107. /* ----------------
  108.  * execute the Hash node, to build the hash table
  109.  * ----------------
  110.  */
  111. hashNode->hashstate->hashtable = hashtable;
  112. innerTupleSlot = ExecProcNode((Plan *) hashNode, (Plan *) node);
  113. }
  114. node->hashdone = true;
  115. /* ----------------
  116.  * Open temp files for outer batches, if needed.
  117.  * Note that file buffers are palloc'd in regular executor context.
  118.  * ----------------
  119.  */
  120. for (i = 0; i < hashtable->nbatch; i++)
  121. {
  122. File tfile = OpenTemporaryFile();
  123. Assert(tfile >= 0);
  124. hashtable->outerBatchFile[i] = BufFileCreate(tfile);
  125. }
  126. }
  127. else if (hashtable == NULL)
  128. return NULL;
  129. /* ----------------
  130.  * Now get an outer tuple and probe into the hash table for matches
  131.  * ----------------
  132.  */
  133. outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
  134. outerVar = get_leftop(clause);
  135. for (;;)
  136. {
  137. /*
  138.  * if the current outer tuple is nil, get a new one
  139.  */
  140. if (TupIsNull(outerTupleSlot))
  141. {
  142. outerTupleSlot = ExecHashJoinOuterGetTuple(outerNode,
  143.    (Plan *) node,
  144.    hjstate);
  145. if (TupIsNull(outerTupleSlot))
  146. {
  147. /*
  148.  * when the last batch runs out, clean up and exit
  149.  */
  150. ExecHashTableDestroy(hashtable);
  151. hjstate->hj_HashTable = NULL;
  152. return NULL;
  153. }
  154. /*
  155.  * now we have an outer tuple, find the corresponding bucket
  156.  * for this tuple from the hash table
  157.  */
  158. econtext->ecxt_outertuple = outerTupleSlot;
  159. hjstate->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext,
  160. outerVar);
  161. hjstate->hj_CurTuple = NULL;
  162. /* ----------------
  163.  * Now we've got an outer tuple and the corresponding hash bucket,
  164.  * but this tuple may not belong to the current batch.
  165.  * This need only be checked in the first pass.
  166.  * ----------------
  167.  */
  168. if (hashtable->curbatch == 0)
  169. {
  170. int batch = ExecHashJoinGetBatch(hjstate->hj_CurBucketNo,
  171.  hashtable);
  172. if (batch > 0)
  173. {
  174. /*
  175.  * Need to postpone this outer tuple to a later batch.
  176.  * Save it in the corresponding outer-batch file.
  177.  */
  178. int batchno = batch - 1;
  179. hashtable->outerBatchSize[batchno]++;
  180. ExecHashJoinSaveTuple(outerTupleSlot->val,
  181.  hashtable->outerBatchFile[batchno]);
  182. ExecClearTuple(outerTupleSlot);
  183. continue; /* loop around for a new outer tuple */
  184. }
  185. }
  186. }
  187. /*
  188.  * OK, scan the selected hash bucket for matches
  189.  */
  190. for (;;)
  191. {
  192. curtuple = ExecScanHashBucket(hjstate,
  193.   hjclauses,
  194.   econtext);
  195. if (curtuple == NULL)
  196. break; /* out of matches */
  197. /*
  198.  * we've got a match, but still need to test qpqual
  199.  */
  200. inntuple = ExecStoreTuple(curtuple,
  201.   hjstate->hj_HashTupleSlot,
  202.   InvalidBuffer,
  203.   false); /* don't pfree this tuple */
  204. econtext->ecxt_innertuple = inntuple;
  205. qualResult = ExecQual(qual, econtext);
  206. /* ----------------
  207.  * if we pass the qual, then save state for next call and
  208.  * have ExecProject form the projection, store it
  209.  * in the tuple table, and return the slot.
  210.  * ----------------
  211.  */
  212. if (qualResult)
  213. {
  214. ProjectionInfo *projInfo;
  215. TupleTableSlot *result;
  216. bool isDone;
  217. hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
  218. projInfo = hjstate->jstate.cs_ProjInfo;
  219. result = ExecProject(projInfo, &isDone);
  220. hjstate->jstate.cs_TupFromTlist = !isDone;
  221. return result;
  222. }
  223. }
  224. /* ----------------
  225.  *  Now the current outer tuple has run out of matches,
  226.  *  so we free it and loop around to get a new outer tuple.
  227.  * ----------------
  228.  */
  229. ExecClearTuple(outerTupleSlot);
  230. }
  231. }
  232. /* ----------------------------------------------------------------
  233.  * ExecInitHashJoin
  234.  *
  235.  * Init routine for HashJoin node.
  236.  * ----------------------------------------------------------------
  237.  */
  238. bool /* return: initialization status */
  239. ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
  240. {
  241. HashJoinState *hjstate;
  242. Plan    *outerNode;
  243. Hash    *hashNode;
  244. /* ----------------
  245.  * assign the node's execution state
  246.  * ----------------
  247.  */
  248. node->join.state = estate;
  249. /* ----------------
  250.  * create state structure
  251.  * ----------------
  252.  */
  253. hjstate = makeNode(HashJoinState);
  254. node->hashjoinstate = hjstate;
  255. /* ----------------
  256.  * Miscellaneous initialization
  257.  *
  258.  *  + assign node's base_id
  259.  *  + assign debugging hooks and
  260.  *  + create expression context for node
  261.  * ----------------
  262.  */
  263. ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent);
  264. ExecAssignExprContext(estate, &hjstate->jstate);
  265. #define HASHJOIN_NSLOTS 2
  266. /* ----------------
  267.  * tuple table initialization
  268.  * ----------------
  269.  */
  270. ExecInitResultTupleSlot(estate, &hjstate->jstate);
  271. ExecInitOuterTupleSlot(estate, hjstate);
  272. /* ----------------
  273.  * initializes child nodes
  274.  * ----------------
  275.  */
  276. outerNode = outerPlan((Plan *) node);
  277. hashNode = (Hash *) innerPlan((Plan *) node);
  278. ExecInitNode(outerNode, estate, (Plan *) node);
  279. ExecInitNode((Plan *) hashNode, estate, (Plan *) node);
  280. /* ----------------
  281.  * now for some voodoo.  our temporary tuple slot
  282.  * is actually the result tuple slot of the Hash node
  283.  * (which is our inner plan). we do this because Hash
  284.  * nodes don't return tuples via ExecProcNode() -- instead
  285.  * the hash join node uses ExecScanHashBucket() to get
  286.  * at the contents of the hash table. -cim 6/9/91
  287.  * ----------------
  288.  */
  289. {
  290. HashState  *hashstate = hashNode->hashstate;
  291. TupleTableSlot *slot = hashstate->cstate.cs_ResultTupleSlot;
  292. hjstate->hj_HashTupleSlot = slot;
  293. }
  294. hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor = ExecGetTupType(outerNode);
  295. /*
  296. hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode);
  297. */
  298. /* ----------------
  299.  * initialize tuple type and projection info
  300.  * ----------------
  301.  */
  302. ExecAssignResultTypeFromTL((Plan *) node, &hjstate->jstate);
  303. ExecAssignProjectionInfo((Plan *) node, &hjstate->jstate);
  304. /* ----------------
  305.  * initialize hash-specific info
  306.  * ----------------
  307.  */
  308. node->hashdone = false;
  309. hjstate->hj_HashTable = (HashJoinTable) NULL;
  310. hjstate->hj_CurBucketNo = 0;
  311. hjstate->hj_CurTuple = (HashJoinTuple) NULL;
  312. hjstate->hj_InnerHashKey = (Var *) NULL;
  313. hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
  314. hjstate->jstate.cs_TupFromTlist = (bool) false;
  315. return TRUE;
  316. }
  317. int
  318. ExecCountSlotsHashJoin(HashJoin *node)
  319. {
  320. return ExecCountSlotsNode(outerPlan(node)) +
  321. ExecCountSlotsNode(innerPlan(node)) +
  322. HASHJOIN_NSLOTS;
  323. }
  324. /* ----------------------------------------------------------------
  325.  * ExecEndHashJoin
  326.  *
  327.  * clean up routine for HashJoin node
  328.  * ----------------------------------------------------------------
  329.  */
  330. void
  331. ExecEndHashJoin(HashJoin *node)
  332. {
  333. HashJoinState *hjstate;
  334. /* ----------------
  335.  * get info from the HashJoin state
  336.  * ----------------
  337.  */
  338. hjstate = node->hashjoinstate;
  339. /* ----------------
  340.  * free hash table in case we end plan before all tuples are retrieved
  341.  * ---------------
  342.  */
  343. if (hjstate->hj_HashTable)
  344. {
  345. ExecHashTableDestroy(hjstate->hj_HashTable);
  346. hjstate->hj_HashTable = NULL;
  347. }
  348. /* ----------------
  349.  * Free the projection info and the scan attribute info
  350.  *
  351.  * Note: we don't ExecFreeResultType(hjstate)
  352.  *   because the rule manager depends on the tupType
  353.  *   returned by ExecMain().  So for now, this
  354.  *   is freed at end-transaction time.  -cim 6/2/91
  355.  * ----------------
  356.  */
  357. ExecFreeProjectionInfo(&hjstate->jstate);
  358. /* ----------------
  359.  * clean up subtrees
  360.  * ----------------
  361.  */
  362. ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
  363. ExecEndNode(innerPlan((Plan *) node), (Plan *) node);
  364. /* ----------------
  365.  * clean out the tuple table
  366.  * ----------------
  367.  */
  368. ExecClearTuple(hjstate->jstate.cs_ResultTupleSlot);
  369. ExecClearTuple(hjstate->hj_OuterTupleSlot);
  370. ExecClearTuple(hjstate->hj_HashTupleSlot);
  371. }
  372. /* ----------------------------------------------------------------
  373.  * ExecHashJoinOuterGetTuple
  374.  *
  375.  * get the next outer tuple for hashjoin: either by
  376.  * executing a plan node as in the first pass, or from
  377.  * the tmp files for the hashjoin batches.
  378.  * ----------------------------------------------------------------
  379.  */
  380. static TupleTableSlot *
  381. ExecHashJoinOuterGetTuple(Plan *node, Plan *parent, HashJoinState *hjstate)
  382. {
  383. HashJoinTable hashtable = hjstate->hj_HashTable;
  384. int curbatch = hashtable->curbatch;
  385. TupleTableSlot *slot;
  386. if (curbatch == 0)
  387. { /* if it is the first pass */
  388. slot = ExecProcNode(node, parent);
  389. if (!TupIsNull(slot))
  390. return slot;
  391. /*
  392.  * We have just reached the end of the first pass. Try to switch
  393.  * to a saved batch.
  394.  */
  395. curbatch = ExecHashJoinNewBatch(hjstate);
  396. }
  397. /*
  398.  * Try to read from a temp file. Loop allows us to advance to new
  399.  * batch as needed.
  400.  */
  401. while (curbatch <= hashtable->nbatch)
  402. {
  403. slot = ExecHashJoinGetSavedTuple(hjstate,
  404.  hashtable->outerBatchFile[curbatch - 1],
  405.  hjstate->hj_OuterTupleSlot);
  406. if (!TupIsNull(slot))
  407. return slot;
  408. curbatch = ExecHashJoinNewBatch(hjstate);
  409. }
  410. /* Out of batches... */
  411. return NULL;
  412. }
  413. /* ----------------------------------------------------------------
  414.  * ExecHashJoinGetSavedTuple
  415.  *
  416.  * read the next tuple from a tmp file
  417.  * ----------------------------------------------------------------
  418.  */
  419. static TupleTableSlot *
  420. ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
  421.   BufFile *file,
  422.   TupleTableSlot *tupleSlot)
  423. {
  424. HeapTupleData htup;
  425. size_t nread;
  426. HeapTuple heapTuple;
  427. nread = BufFileRead(file, (void *) &htup, sizeof(HeapTupleData));
  428. if (nread == 0)
  429. return NULL; /* end of file */
  430. if (nread != sizeof(HeapTupleData))
  431. elog(ERROR, "Read from hashjoin temp file failed");
  432. heapTuple = palloc(HEAPTUPLESIZE + htup.t_len);
  433. memcpy((char *) heapTuple, (char *) &htup, sizeof(HeapTupleData));
  434. heapTuple->t_data = (HeapTupleHeader)
  435. ((char *) heapTuple + HEAPTUPLESIZE);
  436. nread = BufFileRead(file, (void *) heapTuple->t_data, htup.t_len);
  437. if (nread != (size_t) htup.t_len)
  438. elog(ERROR, "Read from hashjoin temp file failed");
  439. return ExecStoreTuple(heapTuple, tupleSlot, InvalidBuffer, true);
  440. }
  441. /* ----------------------------------------------------------------
  442.  * ExecHashJoinNewBatch
  443.  *
  444.  * switch to a new hashjoin batch
  445.  * ----------------------------------------------------------------
  446.  */
  447. static int
  448. ExecHashJoinNewBatch(HashJoinState *hjstate)
  449. {
  450. HashJoinTable hashtable = hjstate->hj_HashTable;
  451. int nbatch = hashtable->nbatch;
  452. int newbatch = hashtable->curbatch + 1;
  453. long    *innerBatchSize = hashtable->innerBatchSize;
  454. long    *outerBatchSize = hashtable->outerBatchSize;
  455. BufFile    *innerFile;
  456. TupleTableSlot *slot;
  457. ExprContext *econtext;
  458. Var    *innerhashkey;
  459. if (newbatch > 1)
  460. {
  461. /*
  462.  * We no longer need the previous outer batch file; close it right
  463.  * away to free disk space.
  464.  */
  465. BufFileClose(hashtable->outerBatchFile[newbatch - 2]);
  466. hashtable->outerBatchFile[newbatch - 2] = NULL;
  467. }
  468. /* --------------
  469.  * We can skip over any batches that are empty on either side.
  470.  * Release associated temp files right away.
  471.  * --------------
  472.  */
  473. while (newbatch <= nbatch &&
  474.    (innerBatchSize[newbatch - 1] == 0L ||
  475. outerBatchSize[newbatch - 1] == 0L))
  476. {
  477. BufFileClose(hashtable->innerBatchFile[newbatch - 1]);
  478. hashtable->innerBatchFile[newbatch - 1] = NULL;
  479. BufFileClose(hashtable->outerBatchFile[newbatch - 1]);
  480. hashtable->outerBatchFile[newbatch - 1] = NULL;
  481. newbatch++;
  482. }
  483. if (newbatch > nbatch)
  484. return newbatch; /* no more batches */
  485. /*
  486.  * Rewind inner and outer batch files for this batch, so that we can
  487.  * start reading them.
  488.  */
  489. if (BufFileSeek(hashtable->outerBatchFile[newbatch - 1], 0L,
  490. SEEK_SET) != 0L)
  491. elog(ERROR, "Failed to rewind hash temp file");
  492. innerFile = hashtable->innerBatchFile[newbatch - 1];
  493. if (BufFileSeek(innerFile, 0L, SEEK_SET) != 0L)
  494. elog(ERROR, "Failed to rewind hash temp file");
  495. /*
  496.  * Reload the hash table with the new inner batch
  497.  */
  498. ExecHashTableReset(hashtable, innerBatchSize[newbatch - 1]);
  499. econtext = hjstate->jstate.cs_ExprContext;
  500. innerhashkey = hjstate->hj_InnerHashKey;
  501. while ((slot = ExecHashJoinGetSavedTuple(hjstate,
  502.  innerFile,
  503.  hjstate->hj_HashTupleSlot))
  504.    && !TupIsNull(slot))
  505. {
  506. econtext->ecxt_innertuple = slot;
  507. ExecHashTableInsert(hashtable, econtext, innerhashkey);
  508. }
  509. /*
  510.  * after we build the hash table, the inner batch file is no longer
  511.  * needed
  512.  */
  513. BufFileClose(innerFile);
  514. hashtable->innerBatchFile[newbatch - 1] = NULL;
  515. hashtable->curbatch = newbatch;
  516. return newbatch;
  517. }
  518. /* ----------------------------------------------------------------
  519.  * ExecHashJoinGetBatch
  520.  *
  521.  * determine the batch number for a bucketno
  522.  * +----------------+-------+-------+ ... +-------+
  523.  * 0   nbuckets  totalbuckets
  524.  * batch  0  1  2    ...
  525.  * ----------------------------------------------------------------
  526.  */
  527. static int
  528. ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable)
  529. {
  530. int b;
  531. if (bucketno < hashtable->nbuckets || hashtable->nbatch == 0)
  532. return 0;
  533. b = (hashtable->nbatch * (bucketno - hashtable->nbuckets)) /
  534. (hashtable->totalbuckets - hashtable->nbuckets);
  535. return b + 1;
  536. }
  537. /* ----------------------------------------------------------------
  538.  * ExecHashJoinSaveTuple
  539.  *
  540.  * save a tuple to a tmp file.
  541.  *
  542.  * The data recorded in the file for each tuple is an image of its
  543.  * HeapTupleData (with meaningless t_data pointer) followed by the
  544.  * HeapTupleHeader and tuple data.
  545.  * ----------------------------------------------------------------
  546.  */
  547. void
  548. ExecHashJoinSaveTuple(HeapTuple heapTuple,
  549.   BufFile *file)
  550. {
  551. size_t written;
  552. written = BufFileWrite(file, (void *) heapTuple, sizeof(HeapTupleData));
  553. if (written != sizeof(HeapTupleData))
  554. elog(ERROR, "Write to hashjoin temp file failed");
  555. written = BufFileWrite(file, (void *) heapTuple->t_data, heapTuple->t_len);
  556. if (written != (size_t) heapTuple->t_len)
  557. elog(ERROR, "Write to hashjoin temp file failed");
  558. }
  559. void
  560. ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
  561. {
  562. HashJoinState *hjstate = node->hashjoinstate;
  563. if (!node->hashdone)
  564. return;
  565. node->hashdone = false;
  566. /*
  567.  * Unfortunately, currently we have to destroy hashtable in all
  568.  * cases...
  569.  */
  570. if (hjstate->hj_HashTable)
  571. {
  572. ExecHashTableDestroy(hjstate->hj_HashTable);
  573. hjstate->hj_HashTable = NULL;
  574. }
  575. hjstate->hj_CurBucketNo = 0;
  576. hjstate->hj_CurTuple = (HashJoinTuple) NULL;
  577. hjstate->hj_InnerHashKey = (Var *) NULL;
  578. hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
  579. hjstate->jstate.cs_TupFromTlist = (bool) false;
  580. /*
  581.  * if chgParam of subnodes is not null then plans will be re-scanned
  582.  * by first ExecProcNode.
  583.  */
  584. if (((Plan *) node)->lefttree->chgParam == NULL)
  585. ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
  586. if (((Plan *) node)->righttree->chgParam == NULL)
  587. ExecReScan(((Plan *) node)->righttree, exprCtxt, (Plan *) node);
  588. }