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

数据库系统

开发平台:

Unix_Linux

  1. /*-------------------------------------------------------------------------
  2.  *
  3.  * nbtpage.c
  4.  *   BTree-specific page management code for the Postgres btree access
  5.  *   method.
  6.  *
  7.  * Copyright (c) 1994, Regents of the University of California
  8.  *
  9.  *
  10.  * IDENTIFICATION
  11.  *   $Header: /usr/local/cvsroot/pgsql/src/backend/access/nbtree/nbtpage.c,v 1.24.2.1 1999/08/02 05:24:41 scrappy Exp $
  12.  *
  13.  * NOTES
  14.  *    Postgres btree pages look like ordinary relation pages. The opaque
  15.  *    data at high addresses includes pointers to left and right siblings
  16.  *    and flag data describing page state.  The first page in a btree, page
  17.  *    zero, is special -- it stores meta-information describing the tree.
  18.  *    Pages one and higher store the actual tree data.
  19.  *
  20.  *-------------------------------------------------------------------------
  21.  */
  22. #include <time.h>
  23. #include "postgres.h"
  24. #include "access/nbtree.h"
  25. #include "miscadmin.h"
  26. #define BTREE_METAPAGE 0
  27. #define BTREE_MAGIC 0x053162
  28. #define BTREE_VERSION 1
  29. typedef struct BTMetaPageData
  30. {
  31. uint32 btm_magic;
  32. uint32 btm_version;
  33. BlockNumber btm_root;
  34. int32 btm_level;
  35. } BTMetaPageData;
  36. #define BTPageGetMeta(p) 
  37. ((BTMetaPageData *) &((PageHeader) p)->pd_linp[0])
  38. extern bool BuildingBtree;
  39. /*
  40.  * We use high-concurrency locking on btrees. There are two cases in
  41.  * which we don't do locking.  One is when we're building the btree.
  42.  * Since the creating transaction has not committed, no one can see
  43.  * the index, and there's no reason to share locks.  The second case
  44.  * is when we're just starting up the database system.  We use some
  45.  * special-purpose initialization code in the relation cache manager
  46.  * (see utils/cache/relcache.c) to allow us to do indexed scans on
  47.  * the system catalogs before we'd normally be able to.  This happens
  48.  * before the lock table is fully initialized, so we can't use it.
  49.  * Strictly speaking, this violates 2pl, but we don't do 2pl on the
  50.  * system catalogs anyway, so I declare this to be okay.
  51.  */
  52. #define USELOCKING (!BuildingBtree && !IsInitProcessingMode())
  53. /*
  54.  * _bt_metapinit() -- Initialize the metadata page of a btree.
  55.  */
  56. void
  57. _bt_metapinit(Relation rel)
  58. {
  59. Buffer buf;
  60. Page pg;
  61. int nblocks;
  62. BTMetaPageData metad;
  63. BTPageOpaque op;
  64. /* can't be sharing this with anyone, now... */
  65. if (USELOCKING)
  66. LockRelation(rel, AccessExclusiveLock);
  67. if ((nblocks = RelationGetNumberOfBlocks(rel)) != 0)
  68. {
  69. elog(ERROR, "Cannot initialize non-empty btree %s",
  70.  RelationGetRelationName(rel));
  71. }
  72. buf = ReadBuffer(rel, P_NEW);
  73. pg = BufferGetPage(buf);
  74. _bt_pageinit(pg, BufferGetPageSize(buf));
  75. metad.btm_magic = BTREE_MAGIC;
  76. metad.btm_version = BTREE_VERSION;
  77. metad.btm_root = P_NONE;
  78. metad.btm_level = 0;
  79. memmove((char *) BTPageGetMeta(pg), (char *) &metad, sizeof(metad));
  80. op = (BTPageOpaque) PageGetSpecialPointer(pg);
  81. op->btpo_flags = BTP_META;
  82. WriteBuffer(buf);
  83. /* all done */
  84. if (USELOCKING)
  85. UnlockRelation(rel, AccessExclusiveLock);
  86. }
  87. #ifdef NOT_USED
  88. /*
  89.  * _bt_checkmeta() -- Verify that the metadata stored in a btree are
  90.  *    reasonable.
  91.  */
  92. void
  93. _bt_checkmeta(Relation rel)
  94. {
  95. Buffer metabuf;
  96. Page metap;
  97. BTMetaPageData *metad;
  98. BTPageOpaque op;
  99. int nblocks;
  100. /* if the relation is empty, this is init time; don't complain */
  101. if ((nblocks = RelationGetNumberOfBlocks(rel)) == 0)
  102. return;
  103. metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
  104. metap = BufferGetPage(metabuf);
  105. op = (BTPageOpaque) PageGetSpecialPointer(metap);
  106. if (!(op->btpo_flags & BTP_META))
  107. {
  108. elog(ERROR, "Invalid metapage for index %s",
  109.  RelationGetRelationName(rel));
  110. }
  111. metad = BTPageGetMeta(metap);
  112. if (metad->btm_magic != BTREE_MAGIC)
  113. {
  114. elog(ERROR, "Index %s is not a btree",
  115.  RelationGetRelationName(rel));
  116. }
  117. if (metad->btm_version != BTREE_VERSION)
  118. {
  119. elog(ERROR, "Version mismatch on %s:  version %d file, version %d code",
  120.  RelationGetRelationName(rel),
  121.  metad->btm_version, BTREE_VERSION);
  122. }
  123. _bt_relbuf(rel, metabuf, BT_READ);
  124. }
  125. #endif
  126. /*
  127.  * _bt_getroot() -- Get the root page of the btree.
  128.  *
  129.  * Since the root page can move around the btree file, we have to read
  130.  * its location from the metadata page, and then read the root page
  131.  * itself.  If no root page exists yet, we have to create one.  The
  132.  * standard class of race conditions exists here; I think I covered
  133.  * them all in the Hopi Indian rain dance of lock requests below.
  134.  *
  135.  * We pass in the access type (BT_READ or BT_WRITE), and return the
  136.  * root page's buffer with the appropriate lock type set.  Reference
  137.  * count on the root page gets bumped by ReadBuffer.  The metadata
  138.  * page is unlocked and unreferenced by this process when this routine
  139.  * returns.
  140.  */
  141. Buffer
  142. _bt_getroot(Relation rel, int access)
  143. {
  144. Buffer metabuf;
  145. Page metapg;
  146. BTPageOpaque metaopaque;
  147. Buffer rootbuf;
  148. Page rootpg;
  149. BTPageOpaque rootopaque;
  150. BlockNumber rootblkno;
  151. BTMetaPageData *metad;
  152. metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
  153. metapg = BufferGetPage(metabuf);
  154. metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
  155. Assert(metaopaque->btpo_flags & BTP_META);
  156. metad = BTPageGetMeta(metapg);
  157. if (metad->btm_magic != BTREE_MAGIC)
  158. {
  159. elog(ERROR, "Index %s is not a btree",
  160.  RelationGetRelationName(rel));
  161. }
  162. if (metad->btm_version != BTREE_VERSION)
  163. {
  164. elog(ERROR, "Version mismatch on %s:  version %d file, version %d code",
  165.  RelationGetRelationName(rel),
  166.  metad->btm_version, BTREE_VERSION);
  167. }
  168. /* if no root page initialized yet, do it */
  169. if (metad->btm_root == P_NONE)
  170. {
  171. /* turn our read lock in for a write lock */
  172. _bt_relbuf(rel, metabuf, BT_READ);
  173. metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE);
  174. metapg = BufferGetPage(metabuf);
  175. metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
  176. Assert(metaopaque->btpo_flags & BTP_META);
  177. metad = BTPageGetMeta(metapg);
  178. /*
  179.  * Race condition: if someone else initialized the metadata
  180.  * between the time we released the read lock and acquired the
  181.  * write lock, above, we want to avoid doing it again.
  182.  */
  183. if (metad->btm_root == P_NONE)
  184. {
  185. /*
  186.  * Get, initialize, write, and leave a lock of the appropriate
  187.  * type on the new root page.  Since this is the first page in
  188.  * the tree, it's a leaf.
  189.  */
  190. rootbuf = _bt_getbuf(rel, P_NEW, BT_WRITE);
  191. rootblkno = BufferGetBlockNumber(rootbuf);
  192. rootpg = BufferGetPage(rootbuf);
  193. metad->btm_root = rootblkno;
  194. metad->btm_level = 1;
  195. _bt_pageinit(rootpg, BufferGetPageSize(rootbuf));
  196. rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpg);
  197. rootopaque->btpo_flags |= (BTP_LEAF | BTP_ROOT);
  198. _bt_wrtnorelbuf(rel, rootbuf);
  199. /* swap write lock for read lock, if appropriate */
  200. if (access != BT_WRITE)
  201. {
  202. LockBuffer(rootbuf, BUFFER_LOCK_UNLOCK);
  203. LockBuffer(rootbuf, BT_READ);
  204. }
  205. /* okay, metadata is correct */
  206. _bt_wrtbuf(rel, metabuf);
  207. }
  208. else
  209. {
  210. /*
  211.  * Metadata initialized by someone else.  In order to
  212.  * guarantee no deadlocks, we have to release the metadata
  213.  * page and start all over again.
  214.  */
  215. _bt_relbuf(rel, metabuf, BT_WRITE);
  216. return _bt_getroot(rel, access);
  217. }
  218. }
  219. else
  220. {
  221. rootblkno = metad->btm_root;
  222. _bt_relbuf(rel, metabuf, BT_READ); /* done with the meta page */
  223. rootbuf = _bt_getbuf(rel, rootblkno, access);
  224. }
  225. /*
  226.  * Race condition: If the root page split between the time we looked
  227.  * at the metadata page and got the root buffer, then we got the wrong
  228.  * buffer.
  229.  */
  230. rootpg = BufferGetPage(rootbuf);
  231. rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpg);
  232. if (!(rootopaque->btpo_flags & BTP_ROOT))
  233. {
  234. /* it happened, try again */
  235. _bt_relbuf(rel, rootbuf, access);
  236. return _bt_getroot(rel, access);
  237. }
  238. /*
  239.  * By here, we have a correct lock on the root block, its reference
  240.  * count is correct, and we have no lock set on the metadata page.
  241.  * Return the root block.
  242.  */
  243. return rootbuf;
  244. }
  245. /*
  246.  * _bt_getbuf() -- Get a buffer by block number for read or write.
  247.  *
  248.  * When this routine returns, the appropriate lock is set on the
  249.  * requested buffer its reference count is correct.
  250.  */
  251. Buffer
  252. _bt_getbuf(Relation rel, BlockNumber blkno, int access)
  253. {
  254. Buffer buf;
  255. Page page;
  256. if (blkno != P_NEW)
  257. {
  258. buf = ReadBuffer(rel, blkno);
  259. LockBuffer(buf, access);
  260. }
  261. else
  262. {
  263. /*
  264.  * Extend bufmgr code is unclean and so we have to use locking
  265.  * here.
  266.  */
  267. LockPage(rel, 0, ExclusiveLock);
  268. buf = ReadBuffer(rel, blkno);
  269. UnlockPage(rel, 0, ExclusiveLock);
  270. blkno = BufferGetBlockNumber(buf);
  271. page = BufferGetPage(buf);
  272. _bt_pageinit(page, BufferGetPageSize(buf));
  273. LockBuffer(buf, access);
  274. }
  275. /* ref count and lock type are correct */
  276. return buf;
  277. }
  278. /*
  279.  * _bt_relbuf() -- release a locked buffer.
  280.  */
  281. void
  282. _bt_relbuf(Relation rel, Buffer buf, int access)
  283. {
  284. LockBuffer(buf, BUFFER_LOCK_UNLOCK);
  285. ReleaseBuffer(buf);
  286. }
  287. /*
  288.  * _bt_wrtbuf() -- write a btree page to disk.
  289.  *
  290.  * This routine releases the lock held on the buffer and our reference
  291.  * to it. It is an error to call _bt_wrtbuf() without a write lock
  292.  * or a reference to the buffer.
  293.  */
  294. void
  295. _bt_wrtbuf(Relation rel, Buffer buf)
  296. {
  297. LockBuffer(buf, BUFFER_LOCK_UNLOCK);
  298. WriteBuffer(buf);
  299. }
  300. /*
  301.  * _bt_wrtnorelbuf() -- write a btree page to disk, but do not release
  302.  *  our reference or lock.
  303.  *
  304.  * It is an error to call _bt_wrtnorelbuf() without a write lock
  305.  * or a reference to the buffer.
  306.  */
  307. void
  308. _bt_wrtnorelbuf(Relation rel, Buffer buf)
  309. {
  310. WriteNoReleaseBuffer(buf);
  311. }
  312. /*
  313.  * _bt_pageinit() -- Initialize a new page.
  314.  */
  315. void
  316. _bt_pageinit(Page page, Size size)
  317. {
  318. /*
  319.  * Cargo_cult programming -- don't really need this to be zero, but
  320.  * creating new pages is an infrequent occurrence and it makes me feel
  321.  * good when I know they're empty.
  322.  */
  323. MemSet(page, 0, size);
  324. PageInit(page, size, sizeof(BTPageOpaqueData));
  325. ((BTPageOpaque) PageGetSpecialPointer(page))->btpo_parent =
  326. InvalidBlockNumber;
  327. }
  328. /*
  329.  * _bt_metaproot() -- Change the root page of the btree.
  330.  *
  331.  * Lehman and Yao require that the root page move around in order to
  332.  * guarantee deadlock-free short-term, fine-granularity locking.  When
  333.  * we split the root page, we record the new parent in the metadata page
  334.  * for the relation.  This routine does the work.
  335.  *
  336.  * No direct preconditions, but if you don't have the a write lock on
  337.  * at least the old root page when you call this, you're making a big
  338.  * mistake.  On exit, metapage data is correct and we no longer have
  339.  * a reference to or lock on the metapage.
  340.  */
  341. void
  342. _bt_metaproot(Relation rel, BlockNumber rootbknum, int level)
  343. {
  344. Buffer metabuf;
  345. Page metap;
  346. BTPageOpaque metaopaque;
  347. BTMetaPageData *metad;
  348. metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE);
  349. metap = BufferGetPage(metabuf);
  350. metaopaque = (BTPageOpaque) PageGetSpecialPointer(metap);
  351. Assert(metaopaque->btpo_flags & BTP_META);
  352. metad = BTPageGetMeta(metap);
  353. metad->btm_root = rootbknum;
  354. if (level == 0) /* called from _do_insert */
  355. metad->btm_level += 1;
  356. else
  357. metad->btm_level = level; /* called from btsort */
  358. _bt_wrtbuf(rel, metabuf);
  359. }
  360. /*
  361.  * _bt_getstackbuf() -- Walk back up the tree one step, and find the item
  362.  *  we last looked at in the parent.
  363.  *
  364.  * This is possible because we save a bit image of the last item
  365.  * we looked at in the parent, and the update algorithm guarantees
  366.  * that if items above us in the tree move, they only move right.
  367.  *
  368.  * Also, re-set bts_blkno & bts_offset if changed and
  369.  * bts_btitem (it may be changed - see _bt_insertonpg).
  370.  */
  371. Buffer
  372. _bt_getstackbuf(Relation rel, BTStack stack, int access)
  373. {
  374. Buffer buf;
  375. BlockNumber blkno;
  376. OffsetNumber start,
  377. offnum,
  378. maxoff;
  379. OffsetNumber i;
  380. Page page;
  381. ItemId itemid;
  382. BTItem item;
  383. BTPageOpaque opaque;
  384. BTItem item_save;
  385. int item_nbytes;
  386. blkno = stack->bts_blkno;
  387. buf = _bt_getbuf(rel, blkno, access);
  388. page = BufferGetPage(buf);
  389. opaque = (BTPageOpaque) PageGetSpecialPointer(page);
  390. maxoff = PageGetMaxOffsetNumber(page);
  391. if (stack->bts_offset == InvalidOffsetNumber ||
  392. maxoff >= stack->bts_offset)
  393. {
  394. /*
  395.  * _bt_insertonpg set bts_offset to InvalidOffsetNumber in the
  396.  * case of concurrent ROOT page split
  397.  */
  398. if (stack->bts_offset == InvalidOffsetNumber)
  399. i = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY;
  400. else
  401. {
  402. itemid = PageGetItemId(page, stack->bts_offset);
  403. item = (BTItem) PageGetItem(page, itemid);
  404. /* if the item is where we left it, we're done */
  405. if (BTItemSame(item, stack->bts_btitem))
  406. {
  407. pfree(stack->bts_btitem);
  408. item_nbytes = ItemIdGetLength(itemid);
  409. item_save = (BTItem) palloc(item_nbytes);
  410. memmove((char *) item_save, (char *) item, item_nbytes);
  411. stack->bts_btitem = item_save;
  412. return buf;
  413. }
  414. i = OffsetNumberNext(stack->bts_offset);
  415. }
  416. /* if the item has just moved right on this page, we're done */
  417. for (;
  418.  i <= maxoff;
  419.  i = OffsetNumberNext(i))
  420. {
  421. itemid = PageGetItemId(page, i);
  422. item = (BTItem) PageGetItem(page, itemid);
  423. /* if the item is where we left it, we're done */
  424. if (BTItemSame(item, stack->bts_btitem))
  425. {
  426. stack->bts_offset = i;
  427. pfree(stack->bts_btitem);
  428. item_nbytes = ItemIdGetLength(itemid);
  429. item_save = (BTItem) palloc(item_nbytes);
  430. memmove((char *) item_save, (char *) item, item_nbytes);
  431. stack->bts_btitem = item_save;
  432. return buf;
  433. }
  434. }
  435. }
  436. /* by here, the item we're looking for moved right at least one page */
  437. for (;;)
  438. {
  439. blkno = opaque->btpo_next;
  440. if (P_RIGHTMOST(opaque))
  441. elog(FATAL, "my bits moved right off the end of the world!");
  442. _bt_relbuf(rel, buf, access);
  443. buf = _bt_getbuf(rel, blkno, access);
  444. page = BufferGetPage(buf);
  445. maxoff = PageGetMaxOffsetNumber(page);
  446. opaque = (BTPageOpaque) PageGetSpecialPointer(page);
  447. /* if we have a right sibling, step over the high key */
  448. start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY;
  449. /* see if it's on this page */
  450. for (offnum = start;
  451.  offnum <= maxoff;
  452.  offnum = OffsetNumberNext(offnum))
  453. {
  454. itemid = PageGetItemId(page, offnum);
  455. item = (BTItem) PageGetItem(page, itemid);
  456. if (BTItemSame(item, stack->bts_btitem))
  457. {
  458. stack->bts_offset = offnum;
  459. stack->bts_blkno = blkno;
  460. pfree(stack->bts_btitem);
  461. item_nbytes = ItemIdGetLength(itemid);
  462. item_save = (BTItem) palloc(item_nbytes);
  463. memmove((char *) item_save, (char *) item, item_nbytes);
  464. stack->bts_btitem = item_save;
  465. return buf;
  466. }
  467. }
  468. }
  469. }
  470. void
  471. _bt_pagedel(Relation rel, ItemPointer tid)
  472. {
  473. Buffer buf;
  474. Page page;
  475. BlockNumber blkno;
  476. OffsetNumber offno;
  477. blkno = ItemPointerGetBlockNumber(tid);
  478. offno = ItemPointerGetOffsetNumber(tid);
  479. buf = _bt_getbuf(rel, blkno, BT_WRITE);
  480. page = BufferGetPage(buf);
  481. PageIndexTupleDelete(page, offno);
  482. /* write the buffer and release the lock */
  483. _bt_wrtbuf(rel, buf);
  484. }