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

数据库系统

开发平台:

Unix_Linux

  1. /*-------------------------------------------------------------------------
  2.  *
  3.  * hash.c
  4.  *   Implementation of Margo Seltzer's Hashing package for postgres.
  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/hash.c,v 1.26.2.1 1999/08/02 05:24:33 scrappy Exp $
  11.  *
  12.  * NOTES
  13.  *   This file contains only the public interface routines.
  14.  *
  15.  *-------------------------------------------------------------------------
  16.  */
  17. #include "postgres.h"
  18. #include "access/genam.h"
  19. #include "access/hash.h"
  20. #include "access/heapam.h"
  21. #include "catalog/index.h"
  22. #include "executor/executor.h"
  23. #include "miscadmin.h"
  24. bool BuildingHash = false;
  25. /*
  26.  * hashbuild() -- build a new hash index.
  27.  *
  28.  * We use a global variable to record the fact that we're creating
  29.  * a new index.  This is used to avoid high-concurrency locking,
  30.  * since the index won't be visible until this transaction commits
  31.  * and since building is guaranteed to be single-threaded.
  32.  */
  33. void
  34. hashbuild(Relation heap,
  35.   Relation index,
  36.   int natts,
  37.   AttrNumber *attnum,
  38.   IndexStrategy istrat,
  39.   uint16 pcount,
  40.   Datum *params,
  41.   FuncIndexInfo *finfo,
  42.   PredInfo *predInfo)
  43. {
  44. HeapScanDesc hscan;
  45. HeapTuple htup;
  46. IndexTuple itup;
  47. TupleDesc htupdesc,
  48. itupdesc;
  49. Datum    *attdata;
  50. bool    *nulls;
  51. InsertIndexResult res;
  52. int nhtups,
  53. nitups;
  54. int i;
  55. HashItem hitem;
  56. Buffer buffer = InvalidBuffer;
  57. #ifndef OMIT_PARTIAL_INDEX
  58. ExprContext *econtext;
  59. TupleTable tupleTable;
  60. TupleTableSlot *slot;
  61. #endif
  62. Oid hrelid,
  63. irelid;
  64. Node    *pred,
  65.    *oldPred;
  66. /* note that this is a new btree */
  67. BuildingHash = true;
  68. pred = predInfo->pred;
  69. oldPred = predInfo->oldPred;
  70. /* initialize the hash index metadata page (if this is a new index) */
  71. if (oldPred == NULL)
  72. _hash_metapinit(index);
  73. /* get tuple descriptors for heap and index relations */
  74. htupdesc = RelationGetDescr(heap);
  75. itupdesc = RelationGetDescr(index);
  76. /* get space for data items that'll appear in the index tuple */
  77. attdata = (Datum *) palloc(natts * sizeof(Datum));
  78. nulls = (bool *) palloc(natts * sizeof(bool));
  79. /*
  80.  * If this is a predicate (partial) index, we will need to evaluate
  81.  * the predicate using ExecQual, which requires the current tuple to
  82.  * be in a slot of a TupleTable.  In addition, ExecQual must have an
  83.  * ExprContext referring to that slot. Here, we initialize dummy
  84.  * TupleTable and ExprContext objects for this purpose. --Nels, Feb
  85.  * '92
  86.  */
  87. #ifndef OMIT_PARTIAL_INDEX
  88. if (pred != NULL || oldPred != NULL)
  89. {
  90. tupleTable = ExecCreateTupleTable(1);
  91. slot = ExecAllocTableSlot(tupleTable);
  92. econtext = makeNode(ExprContext);
  93. FillDummyExprContext(econtext, slot, htupdesc, buffer);
  94. }
  95. else
  96. /* quiet the compiler */
  97. {
  98. econtext = NULL;
  99. tupleTable = 0;
  100. slot = 0;
  101. }
  102. #endif  /* OMIT_PARTIAL_INDEX */
  103. /* build the index */
  104. nhtups = nitups = 0;
  105. /* start a heap scan */
  106. hscan = heap_beginscan(heap, 0, SnapshotNow, 0, (ScanKey) NULL);
  107. while (HeapTupleIsValid(htup = heap_getnext(hscan, 0)))
  108. {
  109. nhtups++;
  110. /*
  111.  * If oldPred != NULL, this is an EXTEND INDEX command, so skip
  112.  * this tuple if it was already in the existing partial index
  113.  */
  114. if (oldPred != NULL)
  115. {
  116. /* SetSlotContents(slot, htup); */
  117. #ifndef OMIT_PARTIAL_INDEX
  118. slot->val = htup;
  119. if (ExecQual((List *) oldPred, econtext) == true)
  120. {
  121. nitups++;
  122. continue;
  123. }
  124. #endif  /* OMIT_PARTIAL_INDEX */
  125. }
  126. /*
  127.  * Skip this tuple if it doesn't satisfy the partial-index
  128.  * predicate
  129.  */
  130. if (pred != NULL)
  131. {
  132. #ifndef OMIT_PARTIAL_INDEX
  133. /* SetSlotContents(slot, htup); */
  134. slot->val = htup;
  135. if (ExecQual((List *) pred, econtext) == false)
  136. continue;
  137. #endif  /* OMIT_PARTIAL_INDEX */
  138. }
  139. nitups++;
  140. /*
  141.  * For the current heap tuple, extract all the attributes we use
  142.  * in this index, and note which are null.
  143.  */
  144. for (i = 1; i <= natts; i++)
  145. {
  146. int attoff;
  147. bool attnull;
  148. /*
  149.  * Offsets are from the start of the tuple, and are
  150.  * zero-based; indices are one-based.  The next call returns i
  151.  * - 1.  That's data hiding for you.
  152.  */
  153. /* attoff = i - 1 */
  154. attoff = AttrNumberGetAttrOffset(i);
  155. /*
  156.  * below, attdata[attoff] set to equal some datum & attnull is
  157.  * changed to indicate whether or not the attribute is null
  158.  * for this tuple
  159.  */
  160. attdata[attoff] = GetIndexValue(htup,
  161. htupdesc,
  162. attoff,
  163. attnum,
  164. finfo,
  165. &attnull);
  166. nulls[attoff] = (attnull ? 'n' : ' ');
  167. }
  168. /* form an index tuple and point it at the heap tuple */
  169. itup = index_formtuple(itupdesc, attdata, nulls);
  170. /*
  171.  * If the single index key is null, we don't insert it into the
  172.  * index.  Hash tables support scans on '='. Relational algebra
  173.  * says that A = B returns null if either A or B is null.  This
  174.  * means that no qualification used in an index scan could ever
  175.  * return true on a null attribute.  It also means that indices
  176.  * can't be used by ISNULL or NOTNULL scans, but that's an
  177.  * artifact of the strategy map architecture chosen in 1986, not
  178.  * of the way nulls are handled here.
  179.  */
  180. if (itup->t_info & INDEX_NULL_MASK)
  181. {
  182. pfree(itup);
  183. continue;
  184. }
  185. itup->t_tid = htup->t_self;
  186. hitem = _hash_formitem(itup);
  187. res = _hash_doinsert(index, hitem);
  188. pfree(hitem);
  189. pfree(itup);
  190. pfree(res);
  191. }
  192. /* okay, all heap tuples are indexed */
  193. heap_endscan(hscan);
  194. if (pred != NULL || oldPred != NULL)
  195. {
  196. #ifndef OMIT_PARTIAL_INDEX
  197. ExecDestroyTupleTable(tupleTable, true);
  198. pfree(econtext);
  199. #endif  /* OMIT_PARTIAL_INDEX */
  200. }
  201. /*
  202.  * Since we just counted the tuples in the heap, we update its stats
  203.  * in pg_class to guarantee that the planner takes advantage of the
  204.  * index we just created. Finally, only update statistics during
  205.  * normal index definitions, not for indices on system catalogs
  206.  * created during bootstrap processing.  We must close the relations
  207.  * before updatings statistics to guarantee that the relcache entries
  208.  * are flushed when we increment the command counter in UpdateStats().
  209.  */
  210. if (IsNormalProcessingMode())
  211. {
  212. hrelid = RelationGetRelid(heap);
  213. irelid = RelationGetRelid(index);
  214. heap_close(heap);
  215. index_close(index);
  216. UpdateStats(hrelid, nhtups, true);
  217. UpdateStats(irelid, nitups, false);
  218. if (oldPred != NULL)
  219. {
  220. if (nitups == nhtups)
  221. pred = NULL;
  222. UpdateIndexPredicate(irelid, oldPred, pred);
  223. }
  224. }
  225. /* be tidy */
  226. pfree(nulls);
  227. pfree(attdata);
  228. /* all done */
  229. BuildingHash = false;
  230. }
  231. /*
  232.  * hashinsert() -- insert an index tuple into a hash table.
  233.  *
  234.  * Hash on the index tuple's key, find the appropriate location
  235.  * for the new tuple, put it there, and return an InsertIndexResult
  236.  * to the caller.
  237.  */
  238. InsertIndexResult
  239. hashinsert(Relation rel, Datum *datum, char *nulls, ItemPointer ht_ctid, Relation heapRel)
  240. {
  241. HashItem hitem;
  242. IndexTuple itup;
  243. InsertIndexResult res;
  244. /* generate an index tuple */
  245. itup = index_formtuple(RelationGetDescr(rel), datum, nulls);
  246. itup->t_tid = *ht_ctid;
  247. if (itup->t_info & INDEX_NULL_MASK)
  248. return (InsertIndexResult) NULL;
  249. hitem = _hash_formitem(itup);
  250. res = _hash_doinsert(rel, hitem);
  251. pfree(hitem);
  252. pfree(itup);
  253. return res;
  254. }
  255. /*
  256.  * hashgettuple() -- Get the next tuple in the scan.
  257.  */
  258. char *
  259. hashgettuple(IndexScanDesc scan, ScanDirection dir)
  260. {
  261. RetrieveIndexResult res;
  262. /*
  263.  * If we've already initialized this scan, we can just advance it in
  264.  * the appropriate direction.  If we haven't done so yet, we call a
  265.  * routine to get the first item in the scan.
  266.  */
  267. if (ItemPointerIsValid(&(scan->currentItemData)))
  268. res = _hash_next(scan, dir);
  269. else
  270. res = _hash_first(scan, dir);
  271. return (char *) res;
  272. }
  273. /*
  274.  * hashbeginscan() -- start a scan on a hash index
  275.  */
  276. char *
  277. hashbeginscan(Relation rel,
  278.   bool fromEnd,
  279.   uint16 keysz,
  280.   ScanKey scankey)
  281. {
  282. IndexScanDesc scan;
  283. HashScanOpaque so;
  284. scan = RelationGetIndexScan(rel, fromEnd, keysz, scankey);
  285. so = (HashScanOpaque) palloc(sizeof(HashScanOpaqueData));
  286. so->hashso_curbuf = so->hashso_mrkbuf = InvalidBuffer;
  287. scan->opaque = so;
  288. scan->flags = 0x0;
  289. /* register scan in case we change pages it's using */
  290. _hash_regscan(scan);
  291. return (char *) scan;
  292. }
  293. /*
  294.  * hashrescan() -- rescan an index relation
  295.  */
  296. void
  297. hashrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey)
  298. {
  299. ItemPointer iptr;
  300. HashScanOpaque so;
  301. so = (HashScanOpaque) scan->opaque;
  302. /* we hold a read lock on the current page in the scan */
  303. if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
  304. {
  305. _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
  306. so->hashso_curbuf = InvalidBuffer;
  307. ItemPointerSetInvalid(iptr);
  308. }
  309. if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
  310. {
  311. _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
  312. so->hashso_mrkbuf = InvalidBuffer;
  313. ItemPointerSetInvalid(iptr);
  314. }
  315. /* reset the scan key */
  316. if (scan->numberOfKeys > 0)
  317. {
  318. memmove(scan->keyData,
  319. scankey,
  320. scan->numberOfKeys * sizeof(ScanKeyData));
  321. }
  322. }
  323. /*
  324.  * hashendscan() -- close down a scan
  325.  */
  326. void
  327. hashendscan(IndexScanDesc scan)
  328. {
  329. ItemPointer iptr;
  330. HashScanOpaque so;
  331. so = (HashScanOpaque) scan->opaque;
  332. /* release any locks we still hold */
  333. if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
  334. {
  335. _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
  336. so->hashso_curbuf = InvalidBuffer;
  337. ItemPointerSetInvalid(iptr);
  338. }
  339. if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
  340. {
  341. if (BufferIsValid(so->hashso_mrkbuf))
  342. _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
  343. so->hashso_mrkbuf = InvalidBuffer;
  344. ItemPointerSetInvalid(iptr);
  345. }
  346. /* don't need scan registered anymore */
  347. _hash_dropscan(scan);
  348. /* be tidy */
  349. pfree(scan->opaque);
  350. }
  351. /*
  352.  * hashmarkpos() -- save current scan position
  353.  *
  354.  */
  355. void
  356. hashmarkpos(IndexScanDesc scan)
  357. {
  358. ItemPointer iptr;
  359. HashScanOpaque so;
  360. /*
  361.  * see if we ever call this code. if we do, then so_mrkbuf a useful
  362.  * element in the scan->opaque structure. if this procedure is never
  363.  * called, so_mrkbuf should be removed from the scan->opaque
  364.  * structure.
  365.  */
  366. elog(NOTICE, "Hashmarkpos() called.");
  367. so = (HashScanOpaque) scan->opaque;
  368. /* release lock on old marked data, if any */
  369. if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
  370. {
  371. _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
  372. so->hashso_mrkbuf = InvalidBuffer;
  373. ItemPointerSetInvalid(iptr);
  374. }
  375. /* bump lock on currentItemData and copy to currentMarkData */
  376. if (ItemPointerIsValid(&(scan->currentItemData)))
  377. {
  378. so->hashso_mrkbuf = _hash_getbuf(scan->relation,
  379.  BufferGetBlockNumber(so->hashso_curbuf),
  380.  HASH_READ);
  381. scan->currentMarkData = scan->currentItemData;
  382. }
  383. }
  384. /*
  385.  * hashrestrpos() -- restore scan to last saved position
  386.  */
  387. void
  388. hashrestrpos(IndexScanDesc scan)
  389. {
  390. ItemPointer iptr;
  391. HashScanOpaque so;
  392. /*
  393.  * see if we ever call this code. if we do, then so_mrkbuf a useful
  394.  * element in the scan->opaque structure. if this procedure is never
  395.  * called, so_mrkbuf should be removed from the scan->opaque
  396.  * structure.
  397.  */
  398. elog(NOTICE, "Hashrestrpos() called.");
  399. so = (HashScanOpaque) scan->opaque;
  400. /* release lock on current data, if any */
  401. if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
  402. {
  403. _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
  404. so->hashso_curbuf = InvalidBuffer;
  405. ItemPointerSetInvalid(iptr);
  406. }
  407. /* bump lock on currentMarkData and copy to currentItemData */
  408. if (ItemPointerIsValid(&(scan->currentMarkData)))
  409. {
  410. so->hashso_curbuf = _hash_getbuf(scan->relation,
  411.  BufferGetBlockNumber(so->hashso_mrkbuf),
  412.  HASH_READ);
  413. scan->currentItemData = scan->currentMarkData;
  414. }
  415. }
  416. /* stubs */
  417. void
  418. hashdelete(Relation rel, ItemPointer tid)
  419. {
  420. /* adjust any active scans that will be affected by this deletion */
  421. _hash_adjscans(rel, tid);
  422. /* delete the data from the page */
  423. _hash_pagedel(rel, tid);
  424. }