1 /*-------------------------------------------------------------------------
6 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * $Header: /cvsroot/pgsql/src/backend/access/transam/xlogutils.c,v 1.26 2003/08/04 02:39:57 momjian Exp $
11 *-------------------------------------------------------------------------
15 #include "access/htup.h"
16 #include "access/xlogutils.h"
17 #include "catalog/pg_database.h"
18 #include "storage/bufpage.h"
19 #include "storage/smgr.h"
20 #include "utils/hsearch.h"
21 #include "utils/relcache.h"
25 * ---------------------------------------------------------------
27 * Index support functions
29 *----------------------------------------------------------------
33 * Check if specified heap tuple was inserted by given
34 * xaction/command and return
37 * - 0 if there is no tuple at all
41 XLogIsOwnerOfTuple(RelFileNode hnode, ItemPointer iptr,
42 TransactionId xid, CommandId cid)
50 reln = XLogOpenRelation(false, RM_HEAP_ID, hnode);
51 if (!RelationIsValid(reln))
54 buffer = ReadBuffer(reln, ItemPointerGetBlockNumber(iptr));
55 if (!BufferIsValid(buffer))
58 LockBuffer(buffer, BUFFER_LOCK_SHARE);
59 page = (Page) BufferGetPage(buffer);
60 if (PageIsNew((PageHeader) page) ||
61 ItemPointerGetOffsetNumber(iptr) > PageGetMaxOffsetNumber(page))
63 UnlockAndReleaseBuffer(buffer);
66 lp = PageGetItemId(page, ItemPointerGetOffsetNumber(iptr));
67 if (!ItemIdIsUsed(lp) || ItemIdDeleted(lp))
69 UnlockAndReleaseBuffer(buffer);
73 htup = (HeapTupleHeader) PageGetItem(page, lp);
75 Assert(PageGetSUI(page) == ThisStartUpID);
76 if (!TransactionIdEquals(HeapTupleHeaderGetXmin(htup), xid) ||
77 HeapTupleHeaderGetCmin(htup) != cid)
79 UnlockAndReleaseBuffer(buffer);
83 UnlockAndReleaseBuffer(buffer);
88 * MUST BE CALLED ONLY ON RECOVERY.
90 * Check if exists valid (inserted by not aborted xaction) heap tuple
91 * for given item pointer
94 XLogIsValidTuple(RelFileNode hnode, ItemPointer iptr)
100 HeapTupleHeader htup;
102 reln = XLogOpenRelation(false, RM_HEAP_ID, hnode);
103 if (!RelationIsValid(reln))
106 buffer = ReadBuffer(reln, ItemPointerGetBlockNumber(iptr));
107 if (!BufferIsValid(buffer))
110 LockBuffer(buffer, BUFFER_LOCK_SHARE);
111 page = (Page) BufferGetPage(buffer);
112 if (PageIsNew((PageHeader) page) ||
113 ItemPointerGetOffsetNumber(iptr) > PageGetMaxOffsetNumber(page))
115 UnlockAndReleaseBuffer(buffer);
119 if (PageGetSUI(page) != ThisStartUpID)
121 Assert(PageGetSUI(page) < ThisStartUpID);
122 UnlockAndReleaseBuffer(buffer);
126 lp = PageGetItemId(page, ItemPointerGetOffsetNumber(iptr));
127 if (!ItemIdIsUsed(lp) || ItemIdDeleted(lp))
129 UnlockAndReleaseBuffer(buffer);
133 htup = (HeapTupleHeader) PageGetItem(page, lp);
135 /* MUST CHECK WASN'T TUPLE INSERTED IN PREV STARTUP */
137 if (!(htup->t_infomask & HEAP_XMIN_COMMITTED))
139 if (htup->t_infomask & HEAP_XMIN_INVALID ||
140 (htup->t_infomask & HEAP_MOVED_IN &&
141 TransactionIdDidAbort(HeapTupleHeaderGetXvac(htup))) ||
142 TransactionIdDidAbort(HeapTupleHeaderGetXmin(htup)))
144 UnlockAndReleaseBuffer(buffer);
149 UnlockAndReleaseBuffer(buffer);
154 * ---------------------------------------------------------------
156 * Storage related support functions
158 *----------------------------------------------------------------
162 XLogReadBuffer(bool extend, Relation reln, BlockNumber blkno)
164 BlockNumber lastblock = RelationGetNumberOfBlocks(reln);
167 if (blkno >= lastblock)
169 buffer = InvalidBuffer;
170 if (extend) /* we do this in recovery only - no locks */
173 while (lastblock <= blkno)
175 if (buffer != InvalidBuffer)
176 ReleaseBuffer(buffer); /* must be WriteBuffer()? */
177 buffer = ReadBuffer(reln, P_NEW);
181 if (buffer != InvalidBuffer)
182 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
186 buffer = ReadBuffer(reln, blkno);
187 if (buffer != InvalidBuffer)
188 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
196 typedef struct XLogRelDesc
198 RelationData reldata;
199 struct XLogRelDesc *lessRecently;
200 struct XLogRelDesc *moreRecently;
203 typedef struct XLogRelCacheEntry
209 static HTAB *_xlrelcache;
210 static XLogRelDesc *_xlrelarr = NULL;
211 static Form_pg_class _xlpgcarr = NULL;
212 static int _xlast = 0;
213 static int _xlcnt = 0;
215 #define _XLOG_RELCACHESIZE 512
218 _xl_init_rel_cache(void)
222 _xlcnt = _XLOG_RELCACHESIZE;
224 _xlrelarr = (XLogRelDesc *) malloc(sizeof(XLogRelDesc) * _xlcnt);
225 memset(_xlrelarr, 0, sizeof(XLogRelDesc) * _xlcnt);
226 _xlpgcarr = (Form_pg_class) malloc(sizeof(FormData_pg_class) * _xlcnt);
227 memset(_xlpgcarr, 0, sizeof(FormData_pg_class) * _xlcnt);
229 _xlrelarr[0].moreRecently = &(_xlrelarr[0]);
230 _xlrelarr[0].lessRecently = &(_xlrelarr[0]);
232 memset(&ctl, 0, sizeof(ctl));
233 ctl.keysize = sizeof(RelFileNode);
234 ctl.entrysize = sizeof(XLogRelCacheEntry);
237 _xlrelcache = hash_create("XLOG relcache", _XLOG_RELCACHESIZE,
238 &ctl, HASH_ELEM | HASH_FUNCTION);
242 _xl_remove_hash_entry(XLogRelDesc *rdesc)
244 Form_pg_class tpgc = rdesc->reldata.rd_rel;
245 XLogRelCacheEntry *hentry;
247 rdesc->lessRecently->moreRecently = rdesc->moreRecently;
248 rdesc->moreRecently->lessRecently = rdesc->lessRecently;
250 hentry = (XLogRelCacheEntry *) hash_search(_xlrelcache,
251 (void *) &(rdesc->reldata.rd_node), HASH_REMOVE, NULL);
253 elog(PANIC, "_xl_remove_hash_entry: file was not found in cache");
255 if (rdesc->reldata.rd_fd >= 0)
256 smgrclose(DEFAULT_SMGR, &(rdesc->reldata));
258 memset(rdesc, 0, sizeof(XLogRelDesc));
259 memset(tpgc, 0, sizeof(FormData_pg_class));
260 rdesc->reldata.rd_rel = tpgc;
266 _xl_new_reldesc(void)
273 _xlrelarr[_xlast].reldata.rd_rel = &(_xlpgcarr[_xlast]);
274 return (&(_xlrelarr[_xlast]));
278 res = _xlrelarr[0].moreRecently;
280 _xl_remove_hash_entry(res);
288 XLogInitRelationCache(void)
291 _xl_init_rel_cache();
295 XLogCloseRelationCache(void)
297 HASH_SEQ_STATUS status;
298 XLogRelCacheEntry *hentry;
300 DestroyDummyCaches();
305 hash_seq_init(&status, _xlrelcache);
307 while ((hentry = (XLogRelCacheEntry *) hash_seq_search(&status)) != NULL)
308 _xl_remove_hash_entry(hentry->rdesc);
310 hash_destroy(_xlrelcache);
319 XLogOpenRelation(bool redo, RmgrId rmid, RelFileNode rnode)
322 XLogRelCacheEntry *hentry;
325 hentry = (XLogRelCacheEntry *)
326 hash_search(_xlrelcache, (void *) &rnode, HASH_FIND, NULL);
332 res->lessRecently->moreRecently = res->moreRecently;
333 res->moreRecently->lessRecently = res->lessRecently;
337 res = _xl_new_reldesc();
339 sprintf(RelationGetRelationName(&(res->reldata)), "%u", rnode.relNode);
341 /* unexisting DB id */
342 res->reldata.rd_lockInfo.lockRelId.dbId = RecoveryDb;
343 res->reldata.rd_lockInfo.lockRelId.relId = rnode.relNode;
344 res->reldata.rd_node = rnode;
346 hentry = (XLogRelCacheEntry *)
347 hash_search(_xlrelcache, (void *) &rnode, HASH_ENTER, &found);
350 elog(PANIC, "XLogOpenRelation: out of memory for cache");
353 elog(PANIC, "XLogOpenRelation: file found on insert into cache");
357 res->reldata.rd_targblock = InvalidBlockNumber;
358 res->reldata.rd_fd = -1;
359 res->reldata.rd_fd = smgropen(DEFAULT_SMGR, &(res->reldata),
360 true /* allow failure */ );
363 res->moreRecently = &(_xlrelarr[0]);
364 res->lessRecently = _xlrelarr[0].lessRecently;
365 _xlrelarr[0].lessRecently = res;
366 res->lessRecently->moreRecently = res;
368 if (res->reldata.rd_fd < 0) /* file doesn't exist */
371 return (&(res->reldata));