]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/xlogutils.c
Update copyrights to 2003.
[postgresql] / src / backend / access / transam / xlogutils.c
1 /*-------------------------------------------------------------------------
2  *
3  * xlogutils.c
4  *
5  *
6  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * $Header: /cvsroot/pgsql/src/backend/access/transam/xlogutils.c,v 1.26 2003/08/04 02:39:57 momjian Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14
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"
22
23
24 /*
25  * ---------------------------------------------------------------
26  *
27  * Index support functions
28  *
29  *----------------------------------------------------------------
30  */
31
32 /*
33  * Check if specified heap tuple was inserted by given
34  * xaction/command and return
35  *
36  * - -1 if not
37  * - 0  if there is no tuple at all
38  * - 1  if yes
39  */
40 int
41 XLogIsOwnerOfTuple(RelFileNode hnode, ItemPointer iptr,
42                                    TransactionId xid, CommandId cid)
43 {
44         Relation        reln;
45         Buffer          buffer;
46         Page            page;
47         ItemId          lp;
48         HeapTupleHeader htup;
49
50         reln = XLogOpenRelation(false, RM_HEAP_ID, hnode);
51         if (!RelationIsValid(reln))
52                 return (0);
53
54         buffer = ReadBuffer(reln, ItemPointerGetBlockNumber(iptr));
55         if (!BufferIsValid(buffer))
56                 return (0);
57
58         LockBuffer(buffer, BUFFER_LOCK_SHARE);
59         page = (Page) BufferGetPage(buffer);
60         if (PageIsNew((PageHeader) page) ||
61                 ItemPointerGetOffsetNumber(iptr) > PageGetMaxOffsetNumber(page))
62         {
63                 UnlockAndReleaseBuffer(buffer);
64                 return (0);
65         }
66         lp = PageGetItemId(page, ItemPointerGetOffsetNumber(iptr));
67         if (!ItemIdIsUsed(lp) || ItemIdDeleted(lp))
68         {
69                 UnlockAndReleaseBuffer(buffer);
70                 return (0);
71         }
72
73         htup = (HeapTupleHeader) PageGetItem(page, lp);
74
75         Assert(PageGetSUI(page) == ThisStartUpID);
76         if (!TransactionIdEquals(HeapTupleHeaderGetXmin(htup), xid) ||
77                 HeapTupleHeaderGetCmin(htup) != cid)
78         {
79                 UnlockAndReleaseBuffer(buffer);
80                 return (-1);
81         }
82
83         UnlockAndReleaseBuffer(buffer);
84         return (1);
85 }
86
87 /*
88  * MUST BE CALLED ONLY ON RECOVERY.
89  *
90  * Check if exists valid (inserted by not aborted xaction) heap tuple
91  * for given item pointer
92  */
93 bool
94 XLogIsValidTuple(RelFileNode hnode, ItemPointer iptr)
95 {
96         Relation        reln;
97         Buffer          buffer;
98         Page            page;
99         ItemId          lp;
100         HeapTupleHeader htup;
101
102         reln = XLogOpenRelation(false, RM_HEAP_ID, hnode);
103         if (!RelationIsValid(reln))
104                 return (false);
105
106         buffer = ReadBuffer(reln, ItemPointerGetBlockNumber(iptr));
107         if (!BufferIsValid(buffer))
108                 return (false);
109
110         LockBuffer(buffer, BUFFER_LOCK_SHARE);
111         page = (Page) BufferGetPage(buffer);
112         if (PageIsNew((PageHeader) page) ||
113                 ItemPointerGetOffsetNumber(iptr) > PageGetMaxOffsetNumber(page))
114         {
115                 UnlockAndReleaseBuffer(buffer);
116                 return (false);
117         }
118
119         if (PageGetSUI(page) != ThisStartUpID)
120         {
121                 Assert(PageGetSUI(page) < ThisStartUpID);
122                 UnlockAndReleaseBuffer(buffer);
123                 return (true);
124         }
125
126         lp = PageGetItemId(page, ItemPointerGetOffsetNumber(iptr));
127         if (!ItemIdIsUsed(lp) || ItemIdDeleted(lp))
128         {
129                 UnlockAndReleaseBuffer(buffer);
130                 return (false);
131         }
132
133         htup = (HeapTupleHeader) PageGetItem(page, lp);
134
135         /* MUST CHECK WASN'T TUPLE INSERTED IN PREV STARTUP */
136
137         if (!(htup->t_infomask & HEAP_XMIN_COMMITTED))
138         {
139                 if (htup->t_infomask & HEAP_XMIN_INVALID ||
140                         (htup->t_infomask & HEAP_MOVED_IN &&
141                          TransactionIdDidAbort(HeapTupleHeaderGetXvac(htup))) ||
142                         TransactionIdDidAbort(HeapTupleHeaderGetXmin(htup)))
143                 {
144                         UnlockAndReleaseBuffer(buffer);
145                         return (false);
146                 }
147         }
148
149         UnlockAndReleaseBuffer(buffer);
150         return (true);
151 }
152
153 /*
154  * ---------------------------------------------------------------
155  *
156  * Storage related support functions
157  *
158  *----------------------------------------------------------------
159  */
160
161 Buffer
162 XLogReadBuffer(bool extend, Relation reln, BlockNumber blkno)
163 {
164         BlockNumber lastblock = RelationGetNumberOfBlocks(reln);
165         Buffer          buffer;
166
167         if (blkno >= lastblock)
168         {
169                 buffer = InvalidBuffer;
170                 if (extend)                             /* we do this in recovery only - no locks */
171                 {
172                         Assert(InRecovery);
173                         while (lastblock <= blkno)
174                         {
175                                 if (buffer != InvalidBuffer)
176                                         ReleaseBuffer(buffer);          /* must be WriteBuffer()? */
177                                 buffer = ReadBuffer(reln, P_NEW);
178                                 lastblock++;
179                         }
180                 }
181                 if (buffer != InvalidBuffer)
182                         LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
183                 return (buffer);
184         }
185
186         buffer = ReadBuffer(reln, blkno);
187         if (buffer != InvalidBuffer)
188                 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
189         return (buffer);
190 }
191
192 /*
193  * "Relation" cache
194  */
195
196 typedef struct XLogRelDesc
197 {
198         RelationData reldata;
199         struct XLogRelDesc *lessRecently;
200         struct XLogRelDesc *moreRecently;
201 } XLogRelDesc;
202
203 typedef struct XLogRelCacheEntry
204 {
205         RelFileNode rnode;
206         XLogRelDesc *rdesc;
207 } XLogRelCacheEntry;
208
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;
214
215 #define _XLOG_RELCACHESIZE      512
216
217 static void
218 _xl_init_rel_cache(void)
219 {
220         HASHCTL         ctl;
221
222         _xlcnt = _XLOG_RELCACHESIZE;
223         _xlast = 0;
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);
228
229         _xlrelarr[0].moreRecently = &(_xlrelarr[0]);
230         _xlrelarr[0].lessRecently = &(_xlrelarr[0]);
231
232         memset(&ctl, 0, sizeof(ctl));
233         ctl.keysize = sizeof(RelFileNode);
234         ctl.entrysize = sizeof(XLogRelCacheEntry);
235         ctl.hash = tag_hash;
236
237         _xlrelcache = hash_create("XLOG relcache", _XLOG_RELCACHESIZE,
238                                                           &ctl, HASH_ELEM | HASH_FUNCTION);
239 }
240
241 static void
242 _xl_remove_hash_entry(XLogRelDesc *rdesc)
243 {
244         Form_pg_class tpgc = rdesc->reldata.rd_rel;
245         XLogRelCacheEntry *hentry;
246
247         rdesc->lessRecently->moreRecently = rdesc->moreRecently;
248         rdesc->moreRecently->lessRecently = rdesc->lessRecently;
249
250         hentry = (XLogRelCacheEntry *) hash_search(_xlrelcache,
251                                   (void *) &(rdesc->reldata.rd_node), HASH_REMOVE, NULL);
252         if (hentry == NULL)
253                 elog(PANIC, "_xl_remove_hash_entry: file was not found in cache");
254
255         if (rdesc->reldata.rd_fd >= 0)
256                 smgrclose(DEFAULT_SMGR, &(rdesc->reldata));
257
258         memset(rdesc, 0, sizeof(XLogRelDesc));
259         memset(tpgc, 0, sizeof(FormData_pg_class));
260         rdesc->reldata.rd_rel = tpgc;
261
262         return;
263 }
264
265 static XLogRelDesc *
266 _xl_new_reldesc(void)
267 {
268         XLogRelDesc *res;
269
270         _xlast++;
271         if (_xlast < _xlcnt)
272         {
273                 _xlrelarr[_xlast].reldata.rd_rel = &(_xlpgcarr[_xlast]);
274                 return (&(_xlrelarr[_xlast]));
275         }
276
277         /* reuse */
278         res = _xlrelarr[0].moreRecently;
279
280         _xl_remove_hash_entry(res);
281
282         _xlast--;
283         return (res);
284 }
285
286
287 void
288 XLogInitRelationCache(void)
289 {
290         CreateDummyCaches();
291         _xl_init_rel_cache();
292 }
293
294 void
295 XLogCloseRelationCache(void)
296 {
297         HASH_SEQ_STATUS status;
298         XLogRelCacheEntry *hentry;
299
300         DestroyDummyCaches();
301
302         if (!_xlrelarr)
303                 return;
304
305         hash_seq_init(&status, _xlrelcache);
306
307         while ((hentry = (XLogRelCacheEntry *) hash_seq_search(&status)) != NULL)
308                 _xl_remove_hash_entry(hentry->rdesc);
309
310         hash_destroy(_xlrelcache);
311
312         free(_xlrelarr);
313         free(_xlpgcarr);
314
315         _xlrelarr = NULL;
316 }
317
318 Relation
319 XLogOpenRelation(bool redo, RmgrId rmid, RelFileNode rnode)
320 {
321         XLogRelDesc *res;
322         XLogRelCacheEntry *hentry;
323         bool            found;
324
325         hentry = (XLogRelCacheEntry *)
326                 hash_search(_xlrelcache, (void *) &rnode, HASH_FIND, NULL);
327
328         if (hentry)
329         {
330                 res = hentry->rdesc;
331
332                 res->lessRecently->moreRecently = res->moreRecently;
333                 res->moreRecently->lessRecently = res->lessRecently;
334         }
335         else
336         {
337                 res = _xl_new_reldesc();
338
339                 sprintf(RelationGetRelationName(&(res->reldata)), "%u", rnode.relNode);
340
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;
345
346                 hentry = (XLogRelCacheEntry *)
347                         hash_search(_xlrelcache, (void *) &rnode, HASH_ENTER, &found);
348
349                 if (hentry == NULL)
350                         elog(PANIC, "XLogOpenRelation: out of memory for cache");
351
352                 if (found)
353                         elog(PANIC, "XLogOpenRelation: file found on insert into cache");
354
355                 hentry->rdesc = res;
356
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 */ );
361         }
362
363         res->moreRecently = &(_xlrelarr[0]);
364         res->lessRecently = _xlrelarr[0].lessRecently;
365         _xlrelarr[0].lessRecently = res;
366         res->lessRecently->moreRecently = res;
367
368         if (res->reldata.rd_fd < 0) /* file doesn't exist */
369                 return (NULL);
370
371         return (&(res->reldata));
372 }