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

数据库系统

开发平台:

Unix_Linux

  1. /*-------------------------------------------------------------------------
  2.  *
  3.  * nodeHash.c
  4.  *   Routines to hash relations for hashjoin
  5.  *
  6.  * Copyright (c) 1994, Regents of the University of California
  7.  *
  8.  *
  9.  * $Id: nodeHash.c,v 1.36 1999/05/25 16:08:41 momjian Exp $
  10.  *
  11.  *-------------------------------------------------------------------------
  12.  */
  13. /*
  14.  * INTERFACE ROUTINES
  15.  * ExecHash - generate an in-memory hash table of the relation
  16.  * ExecInitHash - initialize node and subnodes..
  17.  * ExecEndHash - shutdown node and subnodes
  18.  *
  19.  */
  20. #include <sys/types.h>
  21. #include <stdio.h>
  22. #include <math.h>
  23. #include <string.h>
  24. #include "postgres.h"
  25. #include "miscadmin.h"
  26. #include "executor/execdebug.h"
  27. #include "executor/executor.h"
  28. #include "executor/nodeHash.h"
  29. #include "executor/nodeHashjoin.h"
  30. #include "utils/hsearch.h"
  31. #include "utils/portal.h"
  32. extern int SortMem;
  33. static int hashFunc(Datum key, int len, bool byVal);
  34. /* ----------------------------------------------------------------
  35.  * ExecHash
  36.  *
  37.  * build hash table for hashjoin, all do partitioning if more
  38.  * than one batches are required.
  39.  * ----------------------------------------------------------------
  40.  */
  41. TupleTableSlot *
  42. ExecHash(Hash *node)
  43. {
  44. EState    *estate;
  45. HashState  *hashstate;
  46. Plan    *outerNode;
  47. Var    *hashkey;
  48. HashJoinTable hashtable;
  49. TupleTableSlot *slot;
  50. ExprContext *econtext;
  51. int nbatch;
  52. int i;
  53. /* ----------------
  54.  * get state info from node
  55.  * ----------------
  56.  */
  57. hashstate = node->hashstate;
  58. estate = node->plan.state;
  59. outerNode = outerPlan(node);
  60. hashtable = hashstate->hashtable;
  61. if (hashtable == NULL)
  62. elog(ERROR, "ExecHash: hash table is NULL.");
  63. nbatch = hashtable->nbatch;
  64. if (nbatch > 0)
  65. {
  66. /* ----------------
  67.  * Open temp files for inner batches, if needed.
  68.  * Note that file buffers are palloc'd in regular executor context.
  69.  * ----------------
  70.  */
  71. for (i = 0; i < nbatch; i++)
  72. {
  73. File tfile = OpenTemporaryFile();
  74. Assert(tfile >= 0);
  75. hashtable->innerBatchFile[i] = BufFileCreate(tfile);
  76. }
  77. }
  78. /* ----------------
  79.  * set expression context
  80.  * ----------------
  81.  */
  82. hashkey = node->hashkey;
  83. econtext = hashstate->cstate.cs_ExprContext;
  84. /* ----------------
  85.  * get all inner tuples and insert into the hash table (or temp files)
  86.  * ----------------
  87.  */
  88. for (;;)
  89. {
  90. slot = ExecProcNode(outerNode, (Plan *) node);
  91. if (TupIsNull(slot))
  92. break;
  93. econtext->ecxt_innertuple = slot;
  94. ExecHashTableInsert(hashtable, econtext, hashkey);
  95. ExecClearTuple(slot);
  96. }
  97. /* ---------------------
  98.  * Return the slot so that we have the tuple descriptor
  99.  * when we need to save/restore them. -Jeff 11 July 1991
  100.  * ---------------------
  101.  */
  102. return slot;
  103. }
  104. /* ----------------------------------------------------------------
  105.  * ExecInitHash
  106.  *
  107.  * Init routine for Hash node
  108.  * ----------------------------------------------------------------
  109.  */
  110. bool
  111. ExecInitHash(Hash *node, EState *estate, Plan *parent)
  112. {
  113. HashState  *hashstate;
  114. Plan    *outerPlan;
  115. SO1_printf("ExecInitHash: %sn",
  116.    "initializing hash node");
  117. /* ----------------
  118.  * assign the node's execution state
  119.  * ----------------
  120.  */
  121. node->plan.state = estate;
  122. /* ----------------
  123.  * create state structure
  124.  * ----------------
  125.  */
  126. hashstate = makeNode(HashState);
  127. node->hashstate = hashstate;
  128. hashstate->hashtable = NULL;
  129. /* ----------------
  130.  * Miscellaneous initialization
  131.  *
  132.  *  + assign node's base_id
  133.  *  + assign debugging hooks and
  134.  *  + create expression context for node
  135.  * ----------------
  136.  */
  137. ExecAssignNodeBaseInfo(estate, &hashstate->cstate, parent);
  138. ExecAssignExprContext(estate, &hashstate->cstate);
  139. /* ----------------
  140.  * initialize our result slot
  141.  * ----------------
  142.  */
  143. ExecInitResultTupleSlot(estate, &hashstate->cstate);
  144. /* ----------------
  145.  * initializes child nodes
  146.  * ----------------
  147.  */
  148. outerPlan = outerPlan(node);
  149. ExecInitNode(outerPlan, estate, (Plan *) node);
  150. /* ----------------
  151.  * initialize tuple type. no need to initialize projection
  152.  * info because this node doesn't do projections
  153.  * ----------------
  154.  */
  155. ExecAssignResultTypeFromOuterPlan((Plan *) node, &hashstate->cstate);
  156. hashstate->cstate.cs_ProjInfo = NULL;
  157. return TRUE;
  158. }
  159. int
  160. ExecCountSlotsHash(Hash *node)
  161. {
  162. #define HASH_NSLOTS 1
  163. return ExecCountSlotsNode(outerPlan(node)) +
  164. ExecCountSlotsNode(innerPlan(node)) +
  165. HASH_NSLOTS;
  166. }
  167. /* ---------------------------------------------------------------
  168.  * ExecEndHash
  169.  *
  170.  * clean up routine for Hash node
  171.  * ----------------------------------------------------------------
  172.  */
  173. void
  174. ExecEndHash(Hash *node)
  175. {
  176. HashState  *hashstate;
  177. Plan    *outerPlan;
  178. /* ----------------
  179.  * get info from the hash state
  180.  * ----------------
  181.  */
  182. hashstate = node->hashstate;
  183. /* ----------------
  184.  * free projection info.  no need to free result type info
  185.  * because that came from the outer plan...
  186.  * ----------------
  187.  */
  188. ExecFreeProjectionInfo(&hashstate->cstate);
  189. /* ----------------
  190.  * shut down the subplan
  191.  * ----------------
  192.  */
  193. outerPlan = outerPlan(node);
  194. ExecEndNode(outerPlan, (Plan *) node);
  195. }
  196. /* ----------------------------------------------------------------
  197.  * ExecHashTableCreate
  198.  *
  199.  * create a hashtable in shared memory for hashjoin.
  200.  * ----------------------------------------------------------------
  201.  */
  202. #define NTUP_PER_BUCKET 10
  203. #define FUDGE_FAC 2.0
  204. HashJoinTable
  205. ExecHashTableCreate(Hash *node)
  206. {
  207. Plan    *outerNode;
  208. int ntuples;
  209. int tupsize;
  210. double inner_rel_bytes;
  211. double hash_table_bytes;
  212. int nbatch;
  213. HashJoinTable hashtable;
  214. int nbuckets;
  215. int totalbuckets;
  216. int bucketsize;
  217. int i;
  218. Portal myPortal;
  219. char myPortalName[64];
  220. MemoryContext oldcxt;
  221. /* ----------------
  222.  * Get information about the size of the relation to be hashed
  223.  * (it's the "outer" subtree of this node, but the inner relation of
  224.  * the hashjoin).
  225.  * Caution: this is only the planner's estimates, and so
  226.  * can't be trusted too far.  Apply a healthy fudge factor.
  227.  * ----------------
  228.  */
  229. outerNode = outerPlan(node);
  230. ntuples = outerNode->plan_size;
  231. if (ntuples <= 0) /* force a plausible size if no info */
  232. ntuples = 1000;
  233. /*
  234.  * estimate tupsize based on footprint of tuple in hashtable... but
  235.  * what about palloc overhead?
  236.  */
  237. tupsize = MAXALIGN(outerNode->plan_width) +
  238. MAXALIGN(sizeof(HashJoinTupleData));
  239. inner_rel_bytes = (double) ntuples *tupsize * FUDGE_FAC;
  240. /*
  241.  * Target hashtable size is SortMem kilobytes, but not less than
  242.  * sqrt(estimated inner rel size), so as to avoid horrible
  243.  * performance.
  244.  */
  245. hash_table_bytes = sqrt(inner_rel_bytes);
  246. if (hash_table_bytes < (SortMem * 1024L))
  247. hash_table_bytes = SortMem * 1024L;
  248. /*
  249.  * Count the number of hash buckets we want for the whole relation,
  250.  * for an average bucket load of NTUP_PER_BUCKET (per virtual
  251.  * bucket!).
  252.  */
  253. totalbuckets = (int) ceil((double) ntuples * FUDGE_FAC / NTUP_PER_BUCKET);
  254. /*
  255.  * Count the number of buckets we think will actually fit in the
  256.  * target memory size, at a loading of NTUP_PER_BUCKET (physical
  257.  * buckets). NOTE: FUDGE_FAC here determines the fraction of the
  258.  * hashtable space reserved to allow for nonuniform distribution of
  259.  * hash values. Perhaps this should be a different number from the
  260.  * other uses of FUDGE_FAC, but since we have no real good way to pick
  261.  * either one...
  262.  */
  263. bucketsize = NTUP_PER_BUCKET * tupsize;
  264. nbuckets = (int) (hash_table_bytes / (bucketsize * FUDGE_FAC));
  265. if (nbuckets <= 0)
  266. nbuckets = 1;
  267. if (totalbuckets <= nbuckets)
  268. {
  269. /*
  270.  * We have enough space, so no batching.  In theory we could even
  271.  * reduce nbuckets, but since that could lead to poor behavior if
  272.  * estimated ntuples is much less than reality, it seems better to
  273.  * make more buckets instead of fewer.
  274.  */
  275. totalbuckets = nbuckets;
  276. nbatch = 0;
  277. }
  278. else
  279. {
  280. /*
  281.  * Need to batch; compute how many batches we want to use. Note
  282.  * that nbatch doesn't have to have anything to do with the ratio
  283.  * totalbuckets/nbuckets; in fact, it is the number of groups we
  284.  * will use for the part of the data that doesn't fall into the
  285.  * first nbuckets hash buckets.
  286.  */
  287. nbatch = (int) ceil((inner_rel_bytes - hash_table_bytes) /
  288. hash_table_bytes);
  289. if (nbatch <= 0)
  290. nbatch = 1;
  291. }
  292. /*
  293.  * Now, totalbuckets is the number of (virtual) hashbuckets for the
  294.  * whole relation, and nbuckets is the number of physical hashbuckets
  295.  * we will use in the first pass.  Data falling into the first
  296.  * nbuckets virtual hashbuckets gets handled in the first pass;
  297.  * everything else gets divided into nbatch batches to be processed in
  298.  * additional passes.
  299.  */
  300. #ifdef HJDEBUG
  301. printf("nbatch = %d, totalbuckets = %d, nbuckets = %dn",
  302.    nbatch, totalbuckets, nbuckets);
  303. #endif
  304. /* ----------------
  305.  * Initialize the hash table control block.
  306.  * The hashtable control block is just palloc'd from executor memory.
  307.  * ----------------
  308.  */
  309. hashtable = (HashJoinTable) palloc(sizeof(HashTableData));
  310. hashtable->nbuckets = nbuckets;
  311. hashtable->totalbuckets = totalbuckets;
  312. hashtable->buckets = NULL;
  313. hashtable->nbatch = nbatch;
  314. hashtable->curbatch = 0;
  315. hashtable->innerBatchFile = NULL;
  316. hashtable->outerBatchFile = NULL;
  317. hashtable->innerBatchSize = NULL;
  318. hashtable->outerBatchSize = NULL;
  319. /* ----------------
  320.  * Create a named portal in which to keep the hashtable working storage.
  321.  * Each hashjoin must have its own portal, so be wary of name conflicts.
  322.  * ----------------
  323.  */
  324. i = 0;
  325. do
  326. {
  327. i++;
  328. sprintf(myPortalName, "<hashtable %d>", i);
  329. myPortal = GetPortalByName(myPortalName);
  330. } while (PortalIsValid(myPortal));
  331. myPortal = CreatePortal(myPortalName);
  332. Assert(PortalIsValid(myPortal));
  333. hashtable->myPortal = (void *) myPortal; /* kluge for circular
  334.  * includes */
  335. hashtable->hashCxt = (MemoryContext) PortalGetVariableMemory(myPortal);
  336. hashtable->batchCxt = (MemoryContext) PortalGetHeapMemory(myPortal);
  337. /* Allocate data that will live for the life of the hashjoin */
  338. oldcxt = MemoryContextSwitchTo(hashtable->hashCxt);
  339. if (nbatch > 0)
  340. {
  341. /* ---------------
  342.  * allocate and initialize the file arrays in hashCxt
  343.  * ---------------
  344.  */
  345. hashtable->innerBatchFile = (BufFile **)
  346. palloc(nbatch * sizeof(BufFile *));
  347. hashtable->outerBatchFile = (BufFile **)
  348. palloc(nbatch * sizeof(BufFile *));
  349. hashtable->innerBatchSize = (long *)
  350. palloc(nbatch * sizeof(long));
  351. hashtable->outerBatchSize = (long *)
  352. palloc(nbatch * sizeof(long));
  353. for (i = 0; i < nbatch; i++)
  354. {
  355. hashtable->innerBatchFile[i] = NULL;
  356. hashtable->outerBatchFile[i] = NULL;
  357. hashtable->innerBatchSize[i] = 0;
  358. hashtable->outerBatchSize[i] = 0;
  359. }
  360. /* The files will not be opened until later... */
  361. }
  362. /*
  363.  * Prepare portal for the first-scan space allocations; allocate the
  364.  * hashbucket array therein, and set each bucket "empty".
  365.  */
  366. MemoryContextSwitchTo(hashtable->batchCxt);
  367. StartPortalAllocMode(DefaultAllocMode, 0);
  368. hashtable->buckets = (HashJoinTuple *)
  369. palloc(nbuckets * sizeof(HashJoinTuple));
  370. if (hashtable->buckets == NULL)
  371. elog(ERROR, "Insufficient memory for hash table.");
  372. for (i = 0; i < nbuckets; i++)
  373. hashtable->buckets[i] = NULL;
  374. MemoryContextSwitchTo(oldcxt);
  375. return hashtable;
  376. }
  377. /* ----------------------------------------------------------------
  378.  * ExecHashTableDestroy
  379.  *
  380.  * destroy a hash table
  381.  * ----------------------------------------------------------------
  382.  */
  383. void
  384. ExecHashTableDestroy(HashJoinTable hashtable)
  385. {
  386. int i;
  387. /* Make sure all the temp files are closed */
  388. for (i = 0; i < hashtable->nbatch; i++)
  389. {
  390. if (hashtable->innerBatchFile[i])
  391. BufFileClose(hashtable->innerBatchFile[i]);
  392. if (hashtable->outerBatchFile[i])
  393. BufFileClose(hashtable->outerBatchFile[i]);
  394. }
  395. /* Destroy the portal to release all working memory */
  396. /* cast here is a kluge for circular includes... */
  397. PortalDestroy((Portal *) &hashtable->myPortal);
  398. /* And drop the control block */
  399. pfree(hashtable);
  400. }
  401. /* ----------------------------------------------------------------
  402.  * ExecHashTableInsert
  403.  *
  404.  * insert a tuple into the hash table depending on the hash value
  405.  * it may just go to a tmp file for other batches
  406.  * ----------------------------------------------------------------
  407.  */
  408. void
  409. ExecHashTableInsert(HashJoinTable hashtable,
  410. ExprContext *econtext,
  411. Var *hashkey)
  412. {
  413. int bucketno = ExecHashGetBucket(hashtable, econtext, hashkey);
  414. TupleTableSlot *slot = econtext->ecxt_innertuple;
  415. HeapTuple heapTuple = slot->val;
  416. /* ----------------
  417.  * decide whether to put the tuple in the hash table or a tmp file
  418.  * ----------------
  419.  */
  420. if (bucketno < hashtable->nbuckets)
  421. {
  422. /* ---------------
  423.  * put the tuple in hash table
  424.  * ---------------
  425.  */
  426. HashJoinTuple hashTuple;
  427. int hashTupleSize;
  428. hashTupleSize = MAXALIGN(sizeof(*hashTuple)) + heapTuple->t_len;
  429. hashTuple = (HashJoinTuple) MemoryContextAlloc(hashtable->batchCxt,
  430.    hashTupleSize);
  431. if (hashTuple == NULL)
  432. elog(ERROR, "Insufficient memory for hash table.");
  433. memcpy((char *) &hashTuple->htup,
  434.    (char *) heapTuple,
  435.    sizeof(hashTuple->htup));
  436. hashTuple->htup.t_data = (HeapTupleHeader)
  437. (((char *) hashTuple) + MAXALIGN(sizeof(*hashTuple)));
  438. memcpy((char *) hashTuple->htup.t_data,
  439.    (char *) heapTuple->t_data,
  440.    heapTuple->t_len);
  441. hashTuple->next = hashtable->buckets[bucketno];
  442. hashtable->buckets[bucketno] = hashTuple;
  443. }
  444. else
  445. {
  446. /* -----------------
  447.  * put the tuple into a tmp file for other batches
  448.  * -----------------
  449.  */
  450. int batchno = (hashtable->nbatch * (bucketno - hashtable->nbuckets)) /
  451. (hashtable->totalbuckets - hashtable->nbuckets);
  452. hashtable->innerBatchSize[batchno]++;
  453. ExecHashJoinSaveTuple(heapTuple,
  454.   hashtable->innerBatchFile[batchno]);
  455. }
  456. }
  457. /* ----------------------------------------------------------------
  458.  * ExecHashGetBucket
  459.  *
  460.  * Get the hash value for a tuple
  461.  * ----------------------------------------------------------------
  462.  */
  463. int
  464. ExecHashGetBucket(HashJoinTable hashtable,
  465.   ExprContext *econtext,
  466.   Var *hashkey)
  467. {
  468. int bucketno;
  469. Datum keyval;
  470. bool isNull;
  471. /* ----------------
  472.  * Get the join attribute value of the tuple
  473.  *
  474.  * ...It's quick hack - use ExecEvalExpr instead of ExecEvalVar:
  475.  * hashkey may be T_ArrayRef, not just T_Var. - vadim 04/22/97
  476.  * ----------------
  477.  */
  478. keyval = ExecEvalExpr((Node *) hashkey, econtext, &isNull, NULL);
  479. /*
  480.  * keyval could be null, so we better point it to something valid
  481.  * before trying to run hashFunc on it. --djm 8/17/96
  482.  */
  483. if (isNull)
  484. {
  485. execConstByVal = 0;
  486. execConstLen = 0;
  487. keyval = (Datum) "";
  488. }
  489. /* ------------------
  490.  * compute the hash function
  491.  * ------------------
  492.  */
  493. bucketno = hashFunc(keyval, execConstLen, execConstByVal) % hashtable->totalbuckets;
  494. #ifdef HJDEBUG
  495. if (bucketno >= hashtable->nbuckets)
  496. printf("hash(%d) = %d SAVEDn", keyval, bucketno);
  497. else
  498. printf("hash(%d) = %dn", keyval, bucketno);
  499. #endif
  500. return bucketno;
  501. }
  502. /* ----------------------------------------------------------------
  503.  * ExecScanHashBucket
  504.  *
  505.  * scan a hash bucket of matches
  506.  * ----------------------------------------------------------------
  507.  */
  508. HeapTuple
  509. ExecScanHashBucket(HashJoinState *hjstate,
  510.    List *hjclauses,
  511.    ExprContext *econtext)
  512. {
  513. HashJoinTable hashtable = hjstate->hj_HashTable;
  514. HashJoinTuple hashTuple = hjstate->hj_CurTuple;
  515. /*
  516.  * hj_CurTuple is NULL to start scanning a new bucket, or the address
  517.  * of the last tuple returned from the current bucket.
  518.  */
  519. if (hashTuple == NULL)
  520. hashTuple = hashtable->buckets[hjstate->hj_CurBucketNo];
  521. else
  522. hashTuple = hashTuple->next;
  523. while (hashTuple != NULL)
  524. {
  525. HeapTuple heapTuple = &hashTuple->htup;
  526. TupleTableSlot *inntuple;
  527. bool qualResult;
  528. /* insert hashtable's tuple into exec slot so ExecQual sees it */
  529. inntuple = ExecStoreTuple(heapTuple, /* tuple to store */
  530.   hjstate->hj_HashTupleSlot, /* slot */
  531.   InvalidBuffer,
  532.   false); /* do not pfree this tuple */
  533. econtext->ecxt_innertuple = inntuple;
  534. qualResult = ExecQual(hjclauses, econtext);
  535. if (qualResult)
  536. {
  537. hjstate->hj_CurTuple = hashTuple;
  538. return heapTuple;
  539. }
  540. hashTuple = hashTuple->next;
  541. }
  542. /* ----------------
  543.  * no match
  544.  * ----------------
  545.  */
  546. return NULL;
  547. }
  548. /* ----------------------------------------------------------------
  549.  * hashFunc
  550.  *
  551.  * the hash function, copied from Margo
  552.  * ----------------------------------------------------------------
  553.  */
  554. static int
  555. hashFunc(Datum key, int len, bool byVal)
  556. {
  557. unsigned int h = 0;
  558. unsigned char *k;
  559. if (byVal)
  560. {
  561. /*
  562.  * If it's a by-value data type, use the 'len' least significant
  563.  * bytes of the Datum value.  This should do the right thing on
  564.  * either bigendian or littleendian hardware --- see the Datum
  565.  * access macros in c.h.
  566.  */
  567. while (len-- > 0)
  568. {
  569. h = (h * PRIME1) ^ (key & 0xFF);
  570. key >>= 8;
  571. }
  572. }
  573. else
  574. {
  575. /*
  576.  * If this is a variable length type, then 'k' points to a "struct
  577.  * varlena" and len == -1. NOTE: VARSIZE returns the "real" data
  578.  * length plus the sizeof the "vl_len" attribute of varlena (the
  579.  * length information). 'k' points to the beginning of the varlena
  580.  * struct, so we have to use "VARDATA" to find the beginning of
  581.  * the "real" data.
  582.  */
  583. if (len == -1)
  584. {
  585. len = VARSIZE(key) - VARHDRSZ;
  586. k = (unsigned char *) VARDATA(key);
  587. }
  588. else
  589. k = (unsigned char *) key;
  590. while (len-- > 0)
  591. h = (h * PRIME1) ^ (*k++);
  592. }
  593. return h % PRIME2;
  594. }
  595. /* ----------------------------------------------------------------
  596.  * ExecHashTableReset
  597.  *
  598.  * reset hash table header for new batch
  599.  *
  600.  * ntuples is the number of tuples in the inner relation's batch
  601.  * (which we currently don't actually use...)
  602.  * ----------------------------------------------------------------
  603.  */
  604. void
  605. ExecHashTableReset(HashJoinTable hashtable, long ntuples)
  606. {
  607. MemoryContext oldcxt;
  608. int nbuckets = hashtable->nbuckets;
  609. int i;
  610. /*
  611.  * Release all the hash buckets and tuples acquired in the prior pass,
  612.  * and reinitialize the portal for a new pass.
  613.  */
  614. oldcxt = MemoryContextSwitchTo(hashtable->batchCxt);
  615. EndPortalAllocMode();
  616. StartPortalAllocMode(DefaultAllocMode, 0);
  617. /*
  618.  * We still use the same number of physical buckets as in the first
  619.  * pass. (It could be different; but we already decided how many
  620.  * buckets would be appropriate for the allowed memory, so stick with
  621.  * that number.) We MUST set totalbuckets to equal nbuckets, because
  622.  * from now on no tuples will go out to temp files; there are no more
  623.  * virtual buckets, only real buckets. (This implies that tuples will
  624.  * go into different bucket numbers than they did on the first pass,
  625.  * but that's OK.)
  626.  */
  627. hashtable->totalbuckets = nbuckets;
  628. /* Reallocate and reinitialize the hash bucket headers. */
  629. hashtable->buckets = (HashJoinTuple *)
  630. palloc(nbuckets * sizeof(HashJoinTuple));
  631. if (hashtable->buckets == NULL)
  632. elog(ERROR, "Insufficient memory for hash table.");
  633. for (i = 0; i < nbuckets; i++)
  634. hashtable->buckets[i] = NULL;
  635. MemoryContextSwitchTo(oldcxt);
  636. }
  637. void
  638. ExecReScanHash(Hash *node, ExprContext *exprCtxt, Plan *parent)
  639. {
  640. /*
  641.  * if chgParam of subnode is not null then plan will be re-scanned by
  642.  * first ExecProcNode.
  643.  */
  644. if (((Plan *) node)->lefttree->chgParam == NULL)
  645. ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
  646. }