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

数据库系统

开发平台:

Unix_Linux

  1. /*-------------------------------------------------------------------------
  2.  *
  3.  * hashsearch.c
  4.  *   search code for postgres hash tables
  5.  *
  6.  * Copyright (c) 1994, Regents of the University of California
  7.  *
  8.  *
  9.  * IDENTIFICATION
  10.  *   $Header: /usr/local/cvsroot/pgsql/src/backend/access/hash/hashsearch.c,v 1.17.2.1 1999/08/02 05:24:35 scrappy Exp $
  11.  *
  12.  *-------------------------------------------------------------------------
  13.  */
  14. #include "postgres.h"
  15. #include "access/hash.h"
  16. /*
  17.  * _hash_search() -- Finds the page/bucket that the contains the
  18.  * scankey and loads it into *bufP.  the buffer has a read lock.
  19.  */
  20. void
  21. _hash_search(Relation rel,
  22.  int keysz,
  23.  ScanKey scankey,
  24.  Buffer *bufP,
  25.  HashMetaPage metap)
  26. {
  27. BlockNumber blkno;
  28. Datum keyDatum;
  29. Bucket bucket;
  30. if (scankey == (ScanKey) NULL ||
  31. (keyDatum = scankey[0].sk_argument) == (Datum) NULL)
  32. {
  33. /*
  34.  * If the scankey argument is NULL, all tuples will satisfy the
  35.  * scan so we start the scan at the first bucket (bucket 0).
  36.  */
  37. bucket = 0;
  38. }
  39. else
  40. bucket = _hash_call(rel, metap, keyDatum);
  41. blkno = BUCKET_TO_BLKNO(bucket);
  42. *bufP = _hash_getbuf(rel, blkno, HASH_READ);
  43. }
  44. /*
  45.  * _hash_next() -- Get the next item in a scan.
  46.  *
  47.  * On entry, we have a valid currentItemData in the scan, and a
  48.  * read lock on the page that contains that item. We do not have
  49.  * the page pinned.  We return the next item in the scan. On
  50.  * exit, we have the page containing the next item locked but not
  51.  * pinned.
  52.  */
  53. RetrieveIndexResult
  54. _hash_next(IndexScanDesc scan, ScanDirection dir)
  55. {
  56. Relation rel;
  57. Buffer buf;
  58. Buffer metabuf;
  59. Page page;
  60. OffsetNumber offnum;
  61. RetrieveIndexResult res;
  62. ItemPointer current;
  63. HashItem hitem;
  64. IndexTuple itup;
  65. HashScanOpaque so;
  66. rel = scan->relation;
  67. so = (HashScanOpaque) scan->opaque;
  68. current = &(scan->currentItemData);
  69. metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
  70. /*
  71.  * XXX 10 may 91:  somewhere there's a bug in our management of the
  72.  * cached buffer for this scan.  wei discovered it.  the following is
  73.  * a workaround so he can work until i figure out what's going on.
  74.  */
  75. if (!BufferIsValid(so->hashso_curbuf))
  76. {
  77. so->hashso_curbuf = _hash_getbuf(rel,
  78.   ItemPointerGetBlockNumber(current),
  79.  HASH_READ);
  80. }
  81. /* we still have the buffer pinned and locked */
  82. buf = so->hashso_curbuf;
  83. /*
  84.  * step to next valid tuple.  note that _hash_step releases our lock
  85.  * on 'metabuf'; if we switch to a new 'buf' while looking for the
  86.  * next tuple, we come back with a lock on that buffer.
  87.  */
  88. if (!_hash_step(scan, &buf, dir, metabuf))
  89. return (RetrieveIndexResult) NULL;
  90. /* if we're here, _hash_step found a valid tuple */
  91. current = &(scan->currentItemData);
  92. offnum = ItemPointerGetOffsetNumber(current);
  93. page = BufferGetPage(buf);
  94. _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
  95. hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
  96. itup = &hitem->hash_itup;
  97. res = FormRetrieveIndexResult(current, &(itup->t_tid));
  98. return res;
  99. }
  100. static void
  101. _hash_readnext(Relation rel,
  102.    Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
  103. {
  104. BlockNumber blkno;
  105. blkno = (*opaquep)->hasho_nextblkno;
  106. _hash_relbuf(rel, *bufp, HASH_READ);
  107. *bufp = InvalidBuffer;
  108. if (BlockNumberIsValid(blkno))
  109. {
  110. *bufp = _hash_getbuf(rel, blkno, HASH_READ);
  111. *pagep = BufferGetPage(*bufp);
  112. _hash_checkpage(*pagep, LH_OVERFLOW_PAGE);
  113. *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
  114. Assert(!PageIsEmpty(*pagep));
  115. }
  116. }
  117. static void
  118. _hash_readprev(Relation rel,
  119.    Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
  120. {
  121. BlockNumber blkno;
  122. blkno = (*opaquep)->hasho_prevblkno;
  123. _hash_relbuf(rel, *bufp, HASH_READ);
  124. *bufp = InvalidBuffer;
  125. if (BlockNumberIsValid(blkno))
  126. {
  127. *bufp = _hash_getbuf(rel, blkno, HASH_READ);
  128. *pagep = BufferGetPage(*bufp);
  129. _hash_checkpage(*pagep, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
  130. *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
  131. if (PageIsEmpty(*pagep))
  132. {
  133. Assert((*opaquep)->hasho_flag & LH_BUCKET_PAGE);
  134. _hash_relbuf(rel, *bufp, HASH_READ);
  135. *bufp = InvalidBuffer;
  136. }
  137. }
  138. }
  139. /*
  140.  * _hash_first() -- Find the first item in a scan.
  141.  *
  142.  * Return the RetrieveIndexResult of the first item in the tree that
  143.  * satisfies the qualificatin associated with the scan descriptor. On
  144.  * exit, the page containing the current index tuple is read locked
  145.  * and pinned, and the scan's opaque data entry is updated to
  146.  * include the buffer.
  147.  */
  148. RetrieveIndexResult
  149. _hash_first(IndexScanDesc scan, ScanDirection dir)
  150. {
  151. Relation rel;
  152. Buffer buf;
  153. Buffer metabuf;
  154. Page page;
  155. HashPageOpaque opaque;
  156. HashMetaPage metap;
  157. HashItem hitem;
  158. IndexTuple itup;
  159. ItemPointer current;
  160. OffsetNumber offnum;
  161. RetrieveIndexResult res;
  162. HashScanOpaque so;
  163. rel = scan->relation;
  164. so = (HashScanOpaque) scan->opaque;
  165. current = &(scan->currentItemData);
  166. metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
  167. metap = (HashMetaPage) BufferGetPage(metabuf);
  168. _hash_checkpage((Page) metap, LH_META_PAGE);
  169. /*
  170.  * XXX -- The attribute number stored in the scan key is the attno in
  171.  * the heap relation.  We need to transmogrify this into the index
  172.  * relation attno here.  For the moment, we have hardwired attno == 1.
  173.  */
  174. /* find the correct bucket page and load it into buf */
  175. _hash_search(rel, 1, scan->keyData, &buf, metap);
  176. page = BufferGetPage(buf);
  177. _hash_checkpage(page, LH_BUCKET_PAGE);
  178. opaque = (HashPageOpaque) PageGetSpecialPointer(page);
  179. /*
  180.  * if we are scanning forward, we need to find the first non-empty
  181.  * page (if any) in the bucket chain.  since overflow pages are never
  182.  * empty, this had better be either the bucket page or the first
  183.  * overflow page.
  184.  *
  185.  * if we are scanning backward, we always go all the way to the end of
  186.  * the bucket chain.
  187.  */
  188. if (PageIsEmpty(page))
  189. {
  190. if (BlockNumberIsValid(opaque->hasho_nextblkno))
  191. _hash_readnext(rel, &buf, &page, &opaque);
  192. else
  193. {
  194. ItemPointerSetInvalid(current);
  195. so->hashso_curbuf = InvalidBuffer;
  196. /*
  197.  * If there is no scankeys, all tuples will satisfy the scan -
  198.  * so we continue in _hash_step to get tuples from all
  199.  * buckets. - vadim 04/29/97
  200.  */
  201. if (scan->numberOfKeys >= 1)
  202. {
  203. _hash_relbuf(rel, buf, HASH_READ);
  204. _hash_relbuf(rel, metabuf, HASH_READ);
  205. return (RetrieveIndexResult) NULL;
  206. }
  207. }
  208. }
  209. if (ScanDirectionIsBackward(dir))
  210. {
  211. while (BlockNumberIsValid(opaque->hasho_nextblkno))
  212. _hash_readnext(rel, &buf, &page, &opaque);
  213. }
  214. if (!_hash_step(scan, &buf, dir, metabuf))
  215. return (RetrieveIndexResult) NULL;
  216. /* if we're here, _hash_step found a valid tuple */
  217. current = &(scan->currentItemData);
  218. offnum = ItemPointerGetOffsetNumber(current);
  219. page = BufferGetPage(buf);
  220. _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
  221. hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
  222. itup = &hitem->hash_itup;
  223. res = FormRetrieveIndexResult(current, &(itup->t_tid));
  224. return res;
  225. }
  226. /*
  227.  * _hash_step() -- step to the next valid item in a scan in the bucket.
  228.  *
  229.  * If no valid record exists in the requested direction, return
  230.  * false. Else, return true and set the CurrentItemData for the
  231.  * scan to the right thing.
  232.  *
  233.  * 'bufP' points to the buffer which contains the current page
  234.  * that we'll step through.
  235.  *
  236.  * 'metabuf' is released when this returns.
  237.  */
  238. bool
  239. _hash_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir, Buffer metabuf)
  240. {
  241. Relation rel;
  242. ItemPointer current;
  243. HashScanOpaque so;
  244. int allbuckets;
  245. HashMetaPage metap;
  246. Buffer buf;
  247. Page page;
  248. HashPageOpaque opaque;
  249. OffsetNumber maxoff;
  250. OffsetNumber offnum;
  251. Bucket bucket;
  252. BlockNumber blkno;
  253. HashItem hitem;
  254. IndexTuple itup;
  255. rel = scan->relation;
  256. current = &(scan->currentItemData);
  257. so = (HashScanOpaque) scan->opaque;
  258. allbuckets = (scan->numberOfKeys < 1);
  259. metap = (HashMetaPage) BufferGetPage(metabuf);
  260. _hash_checkpage((Page) metap, LH_META_PAGE);
  261. buf = *bufP;
  262. page = BufferGetPage(buf);
  263. _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
  264. opaque = (HashPageOpaque) PageGetSpecialPointer(page);
  265. /*
  266.  * If _hash_step is called from _hash_first, current will not be
  267.  * valid, so we can't dereference it.  However, in that case, we
  268.  * presumably want to start at the beginning/end of the page...
  269.  */
  270. maxoff = PageGetMaxOffsetNumber(page);
  271. if (ItemPointerIsValid(current))
  272. offnum = ItemPointerGetOffsetNumber(current);
  273. else
  274. offnum = InvalidOffsetNumber;
  275. /*
  276.  * 'offnum' now points to the last tuple we have seen (if any).
  277.  *
  278.  * continue to step through tuples until: 1) we get to the end of the
  279.  * bucket chain or 2) we find a valid tuple.
  280.  */
  281. do
  282. {
  283. bucket = opaque->hasho_bucket;
  284. switch (dir)
  285. {
  286. case ForwardScanDirection:
  287. if (offnum != InvalidOffsetNumber)
  288. {
  289. offnum = OffsetNumberNext(offnum); /* move forward */
  290. }
  291. else
  292. {
  293. offnum = FirstOffsetNumber; /* new page */
  294. }
  295. while (offnum > maxoff)
  296. {
  297. /*
  298.  * either this page is empty (maxoff ==
  299.  * InvalidOffsetNumber) or we ran off the end.
  300.  */
  301. _hash_readnext(rel, &buf, &page, &opaque);
  302. if (BufferIsInvalid(buf))
  303. { /* end of chain */
  304. if (allbuckets && bucket < metap->hashm_maxbucket)
  305. {
  306. ++bucket;
  307. blkno = BUCKET_TO_BLKNO(bucket);
  308. buf = _hash_getbuf(rel, blkno, HASH_READ);
  309. page = BufferGetPage(buf);
  310. _hash_checkpage(page, LH_BUCKET_PAGE);
  311. opaque = (HashPageOpaque) PageGetSpecialPointer(page);
  312. Assert(opaque->hasho_bucket == bucket);
  313. while (PageIsEmpty(page) &&
  314.  BlockNumberIsValid(opaque->hasho_nextblkno))
  315. _hash_readnext(rel, &buf, &page, &opaque);
  316. maxoff = PageGetMaxOffsetNumber(page);
  317. offnum = FirstOffsetNumber;
  318. }
  319. else
  320. {
  321. maxoff = offnum = InvalidOffsetNumber;
  322. break; /* while */
  323. }
  324. }
  325. else
  326. {
  327. /* _hash_readnext never returns an empty page */
  328. maxoff = PageGetMaxOffsetNumber(page);
  329. offnum = FirstOffsetNumber;
  330. }
  331. }
  332. break;
  333. case BackwardScanDirection:
  334. if (offnum != InvalidOffsetNumber)
  335. {
  336. offnum = OffsetNumberPrev(offnum); /* move back */
  337. }
  338. else
  339. {
  340. offnum = maxoff; /* new page */
  341. }
  342. while (offnum < FirstOffsetNumber)
  343. {
  344. /*
  345.  * either this page is empty (offnum ==
  346.  * InvalidOffsetNumber) or we ran off the end.
  347.  */
  348. _hash_readprev(rel, &buf, &page, &opaque);
  349. if (BufferIsInvalid(buf))
  350. { /* end of chain */
  351. if (allbuckets && bucket > 0)
  352. {
  353. --bucket;
  354. blkno = BUCKET_TO_BLKNO(bucket);
  355. buf = _hash_getbuf(rel, blkno, HASH_READ);
  356. page = BufferGetPage(buf);
  357. _hash_checkpage(page, LH_BUCKET_PAGE);
  358. opaque = (HashPageOpaque) PageGetSpecialPointer(page);
  359. Assert(opaque->hasho_bucket == bucket);
  360. while (BlockNumberIsValid(opaque->hasho_nextblkno))
  361. _hash_readnext(rel, &buf, &page, &opaque);
  362. maxoff = offnum = PageGetMaxOffsetNumber(page);
  363. }
  364. else
  365. {
  366. maxoff = offnum = InvalidOffsetNumber;
  367. break; /* while */
  368. }
  369. }
  370. else
  371. {
  372. /* _hash_readprev never returns an empty page */
  373. maxoff = offnum = PageGetMaxOffsetNumber(page);
  374. }
  375. }
  376. break;
  377. default:
  378. /* NoMovementScanDirection */
  379. /* this should not be reached */
  380. break;
  381. }
  382. /* we ran off the end of the world without finding a match */
  383. if (offnum == InvalidOffsetNumber)
  384. {
  385. _hash_relbuf(rel, metabuf, HASH_READ);
  386. *bufP = so->hashso_curbuf = InvalidBuffer;
  387. ItemPointerSetInvalid(current);
  388. return false;
  389. }
  390. /* get ready to check this tuple */
  391. hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
  392. itup = &hitem->hash_itup;
  393. } while (!_hash_checkqual(scan, itup));
  394. /* if we made it to here, we've found a valid tuple */
  395. _hash_relbuf(rel, metabuf, HASH_READ);
  396. blkno = BufferGetBlockNumber(buf);
  397. *bufP = so->hashso_curbuf = buf;
  398. ItemPointerSet(current, blkno, offnum);
  399. return true;
  400. }