1 /*-------------------------------------------------------------------------
6 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 *-------------------------------------------------------------------------
13 #include "access/xlog.h"
14 #include "access/transam.h"
15 #include "access/xact.h"
16 #include "storage/bufpage.h"
17 #include "storage/bufmgr.h"
18 #include "storage/smgr.h"
19 #include "access/htup.h"
20 #include "access/xlogutils.h"
21 #include "catalog/pg_database.h"
22 #include "lib/hasht.h"
23 #include "utils/relcache.h"
26 * ---------------------------------------------------------------
28 * Index support functions
30 *----------------------------------------------------------------
34 * Check if specified heap tuple was inserted by given
35 * xaction/command and return
38 * - 0 if there is no tuple at all
42 XLogIsOwnerOfTuple(RelFileNode hnode, ItemPointer iptr,
43 TransactionId xid, CommandId cid)
51 reln = XLogOpenRelation(false, RM_HEAP_ID, hnode);
52 if (!RelationIsValid(reln))
55 buffer = ReadBuffer(reln, ItemPointerGetBlockNumber(iptr));
56 if (!BufferIsValid(buffer))
59 LockBuffer(buffer, BUFFER_LOCK_SHARE);
60 page = (Page) BufferGetPage(buffer);
61 if (PageIsNew((PageHeader) page) ||
62 ItemPointerGetOffsetNumber(iptr) > PageGetMaxOffsetNumber(page))
64 UnlockAndReleaseBuffer(buffer);
67 lp = PageGetItemId(page, ItemPointerGetOffsetNumber(iptr));
68 if (!ItemIdIsUsed(lp) || ItemIdDeleted(lp))
70 UnlockAndReleaseBuffer(buffer);
74 htup = (HeapTupleHeader) PageGetItem(page, lp);
76 Assert(PageGetSUI(page) == ThisStartUpID);
77 if (htup->t_xmin != xid || htup->t_cmin != 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((TransactionId)htup->t_cmin)) ||
142 TransactionIdDidAbort(htup->t_xmin))
144 UnlockAndReleaseBuffer(buffer);
149 UnlockAndReleaseBuffer(buffer);
154 * Open pg_log in recovery
156 extern Relation LogRelation; /* pg_log relation */
159 XLogOpenLogRelation(void)
161 Relation logRelation;
163 Assert(!LogRelation);
164 logRelation = (Relation) malloc(sizeof(RelationData));
165 memset(logRelation, 0, sizeof(RelationData));
166 logRelation->rd_rel = (Form_pg_class) malloc(sizeof(FormData_pg_class));
167 memset(logRelation->rd_rel, 0, sizeof(FormData_pg_class));
169 sprintf(RelationGetPhysicalRelationName(logRelation), "pg_log");
170 logRelation->rd_node.tblNode = InvalidOid;
171 logRelation->rd_node.relNode = RelOid_pg_log;
172 logRelation->rd_fd = -1;
173 logRelation->rd_fd = smgropen(DEFAULT_SMGR, logRelation, false);
174 if (logRelation->rd_fd < 0)
175 elog(STOP, "XLogOpenLogRelation: failed to open pg_log");
176 LogRelation = logRelation;
180 * ---------------------------------------------------------------
182 * Storage related support functions
184 *----------------------------------------------------------------
188 XLogReadBuffer(bool extend, Relation reln, BlockNumber blkno)
190 BlockNumber lastblock = RelationGetNumberOfBlocks(reln);
193 if (blkno >= lastblock)
195 buffer = InvalidBuffer;
196 if (extend) /* we do this in recovery only - no locks */
199 while (lastblock <= blkno)
201 buffer = ReadBuffer(reln, P_NEW);
205 if (buffer != InvalidBuffer)
206 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
210 buffer = ReadBuffer(reln, blkno);
211 if (buffer != InvalidBuffer)
212 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
220 typedef struct XLogRelDesc
222 RelationData reldata;
223 struct XLogRelDesc *lessRecently;
224 struct XLogRelDesc *moreRecently;
227 typedef struct XLogRelCacheEntry
233 static HTAB *_xlrelcache;
234 static XLogRelDesc *_xlrelarr = NULL;
235 static Form_pg_class _xlpgcarr = NULL;
236 static int _xlast = 0;
237 static int _xlcnt = 0;
238 #define _XLOG_RELCACHESIZE 512
241 _xl_init_rel_cache(void)
245 _xlcnt = _XLOG_RELCACHESIZE;
247 _xlrelarr = (XLogRelDesc*) malloc(sizeof(XLogRelDesc) * _xlcnt);
248 memset(_xlrelarr, 0, sizeof(XLogRelDesc) * _xlcnt);
249 _xlpgcarr = (Form_pg_class) malloc(sizeof(FormData_pg_class) * _xlcnt);
250 memset(_xlpgcarr, 0, sizeof(FormData_pg_class) * _xlcnt);
252 _xlrelarr[0].moreRecently = &(_xlrelarr[0]);
253 _xlrelarr[0].lessRecently = &(_xlrelarr[0]);
255 memset(&ctl, 0, (int) sizeof(ctl));
256 ctl.keysize = sizeof(RelFileNode);
257 ctl.datasize = sizeof(XLogRelDesc*);
260 _xlrelcache = hash_create(_XLOG_RELCACHESIZE, &ctl,
261 HASH_ELEM | HASH_FUNCTION);
265 _xl_remove_hash_entry(XLogRelDesc **edata, Datum dummy)
267 XLogRelCacheEntry *hentry;
269 XLogRelDesc *rdesc = *edata;
270 Form_pg_class tpgc = rdesc->reldata.rd_rel;
272 rdesc->lessRecently->moreRecently = rdesc->moreRecently;
273 rdesc->moreRecently->lessRecently = rdesc->lessRecently;
275 hentry = (XLogRelCacheEntry*) hash_search(_xlrelcache,
276 (char*)&(rdesc->reldata.rd_node), HASH_REMOVE, &found);
279 elog(STOP, "_xl_remove_hash_entry: can't delete from cache");
281 elog(STOP, "_xl_remove_hash_entry: file was not found in cache");
283 if (rdesc->reldata.rd_fd >= 0)
284 smgrclose(DEFAULT_SMGR, &(rdesc->reldata));
286 memset(rdesc, 0, sizeof(XLogRelDesc));
287 memset(tpgc, 0, sizeof(FormData_pg_class));
288 rdesc->reldata.rd_rel = tpgc;
294 _xl_new_reldesc(void)
301 _xlrelarr[_xlast].reldata.rd_rel = &(_xlpgcarr[_xlast]);
302 return(&(_xlrelarr[_xlast]));
306 res = _xlrelarr[0].moreRecently;
308 _xl_remove_hash_entry(&res, 0);
316 XLogInitRelationCache(void)
319 _xl_init_rel_cache();
323 XLogCloseRelationCache(void)
326 DestroyDummyCaches();
331 HashTableWalk(_xlrelcache, (HashtFunc) _xl_remove_hash_entry, 0);
332 hash_destroy(_xlrelcache);
341 XLogOpenRelation(bool redo, RmgrId rmid, RelFileNode rnode)
344 XLogRelCacheEntry *hentry;
347 hentry = (XLogRelCacheEntry*)
348 hash_search(_xlrelcache, (char*)&rnode, HASH_FIND, &found);
351 elog(STOP, "XLogOpenRelation: error in cache");
357 res->lessRecently->moreRecently = res->moreRecently;
358 res->moreRecently->lessRecently = res->lessRecently;
362 res = _xl_new_reldesc();
364 sprintf(RelationGetPhysicalRelationName(&(res->reldata)), "%u", rnode.relNode);
366 /* unexisting DB id */
367 res->reldata.rd_lockInfo.lockRelId.dbId = RecoveryDb;
368 res->reldata.rd_lockInfo.lockRelId.relId = rnode.relNode;
369 res->reldata.rd_node = rnode;
371 hentry = (XLogRelCacheEntry*)
372 hash_search(_xlrelcache, (char*)&rnode, HASH_ENTER, &found);
375 elog(STOP, "XLogOpenRelation: can't insert into cache");
378 elog(STOP, "XLogOpenRelation: file found on insert into cache");
382 res->reldata.rd_fd = -1;
383 res->reldata.rd_fd = smgropen(DEFAULT_SMGR, &(res->reldata),
384 true /* allow failure */);
387 res->moreRecently = &(_xlrelarr[0]);
388 res->lessRecently = _xlrelarr[0].lessRecently;
389 _xlrelarr[0].lessRecently = res;
390 res->lessRecently->moreRecently = res;
392 if (res->reldata.rd_fd < 0) /* file doesn't exist */
395 return(&(res->reldata));