]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/xlogutils.c
Change Copyright from PostgreSQL, Inc to PostgreSQL Global Development Group.
[postgresql] / src / backend / access / transam / xlogutils.c
1 /*-------------------------------------------------------------------------
2  *
3  * xlogutils.c
4  *
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *-------------------------------------------------------------------------
10  */
11
12 #include "postgres.h"
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"
24
25 /*
26  * ---------------------------------------------------------------
27  *
28  * Index support functions
29  *
30  *----------------------------------------------------------------
31  */
32
33 /*
34  * Check if specified heap tuple was inserted by given
35  * xaction/command and return
36  *
37  * - -1 if not
38  * - 0  if there is no tuple at all
39  * - 1  if yes
40  */
41 int
42 XLogIsOwnerOfTuple(RelFileNode hnode, ItemPointer iptr, 
43                                         TransactionId xid, CommandId cid)
44 {
45         Relation                reln;
46         Buffer                  buffer;
47         Page                    page;
48         ItemId                  lp;
49         HeapTupleHeader htup;
50
51         reln = XLogOpenRelation(false, RM_HEAP_ID, hnode);
52         if (!RelationIsValid(reln))
53                 return(0);
54
55         buffer = ReadBuffer(reln, ItemPointerGetBlockNumber(iptr));
56         if (!BufferIsValid(buffer))
57                 return(0);
58
59         LockBuffer(buffer, BUFFER_LOCK_SHARE);
60         page = (Page) BufferGetPage(buffer);
61         if (PageIsNew((PageHeader) page) ||
62                 ItemPointerGetOffsetNumber(iptr) > PageGetMaxOffsetNumber(page))
63         {
64                 UnlockAndReleaseBuffer(buffer);
65                 return(0);
66         }
67         lp = PageGetItemId(page, ItemPointerGetOffsetNumber(iptr));
68         if (!ItemIdIsUsed(lp) || ItemIdDeleted(lp))
69         {
70                 UnlockAndReleaseBuffer(buffer);
71                 return(0);
72         }
73
74         htup = (HeapTupleHeader) PageGetItem(page, lp);
75
76         Assert(PageGetSUI(page) == ThisStartUpID);
77         if (htup->t_xmin != xid || htup->t_cmin != 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((TransactionId)htup->t_cmin)) ||
142                         TransactionIdDidAbort(htup->t_xmin))
143                 {
144                         UnlockAndReleaseBuffer(buffer);
145                         return(false);
146                 }
147         }
148
149         UnlockAndReleaseBuffer(buffer);
150         return(true);
151 }
152
153 /*
154  * Open pg_log in recovery
155  */
156 extern Relation LogRelation;    /* pg_log relation */
157
158 void
159 XLogOpenLogRelation(void)
160 {
161         Relation        logRelation;
162
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));
168
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;
177 }
178
179 /*
180  * ---------------------------------------------------------------
181  *
182  * Storage related support functions
183  *
184  *----------------------------------------------------------------
185  */
186
187 Buffer
188 XLogReadBuffer(bool extend, Relation reln, BlockNumber blkno)
189 {
190         BlockNumber     lastblock = RelationGetNumberOfBlocks(reln);
191         Buffer          buffer;
192
193         if (blkno >= lastblock)
194         {
195                 buffer = InvalidBuffer;
196                 if (extend)             /* we do this in recovery only - no locks */
197                 {
198                         Assert(InRecovery);
199                         while (lastblock <= blkno)
200                         {
201                                 buffer = ReadBuffer(reln, P_NEW);
202                                 lastblock++;
203                         }
204                 }
205                 if (buffer != InvalidBuffer)
206                         LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
207                 return(buffer);
208         }
209
210         buffer = ReadBuffer(reln, blkno);
211         if (buffer != InvalidBuffer)
212                 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
213         return(buffer);
214 }
215
216 /*
217  * "Relation" cache
218  */
219
220 typedef struct XLogRelDesc
221 {
222         RelationData                    reldata;
223         struct XLogRelDesc         *lessRecently;
224         struct XLogRelDesc         *moreRecently;
225 } XLogRelDesc;
226
227 typedef struct XLogRelCacheEntry
228 {
229         RelFileNode             rnode;
230         XLogRelDesc        *rdesc;
231 } XLogRelCacheEntry;
232
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
239
240 static void
241 _xl_init_rel_cache(void)
242 {
243         HASHCTL                 ctl;
244
245         _xlcnt = _XLOG_RELCACHESIZE;
246         _xlast = 0;
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);
251
252         _xlrelarr[0].moreRecently = &(_xlrelarr[0]);
253         _xlrelarr[0].lessRecently = &(_xlrelarr[0]);
254
255         memset(&ctl, 0, (int) sizeof(ctl));
256         ctl.keysize = sizeof(RelFileNode);
257         ctl.datasize = sizeof(XLogRelDesc*);
258         ctl.hash = tag_hash;
259
260         _xlrelcache = hash_create(_XLOG_RELCACHESIZE, &ctl,
261                                                                 HASH_ELEM | HASH_FUNCTION);
262 }
263
264 static void
265 _xl_remove_hash_entry(XLogRelDesc **edata, Datum dummy)
266 {
267         XLogRelCacheEntry          *hentry;
268         bool                                    found;
269         XLogRelDesc                        *rdesc = *edata;
270         Form_pg_class                   tpgc = rdesc->reldata.rd_rel;
271
272         rdesc->lessRecently->moreRecently = rdesc->moreRecently;
273         rdesc->moreRecently->lessRecently = rdesc->lessRecently;
274
275         hentry = (XLogRelCacheEntry*) hash_search(_xlrelcache, 
276                 (char*)&(rdesc->reldata.rd_node), HASH_REMOVE, &found);
277
278         if (hentry == NULL)
279                 elog(STOP, "_xl_remove_hash_entry: can't delete from cache");
280         if (!found)
281                 elog(STOP, "_xl_remove_hash_entry: file was not found in cache");
282
283         if (rdesc->reldata.rd_fd >= 0)
284                 smgrclose(DEFAULT_SMGR, &(rdesc->reldata));
285
286         memset(rdesc, 0, sizeof(XLogRelDesc));
287         memset(tpgc, 0, sizeof(FormData_pg_class));
288         rdesc->reldata.rd_rel = tpgc;
289
290         return;
291 }
292
293 static XLogRelDesc*
294 _xl_new_reldesc(void)
295 {
296         XLogRelDesc        *res;
297
298         _xlast++;
299         if (_xlast < _xlcnt)
300         {
301                 _xlrelarr[_xlast].reldata.rd_rel = &(_xlpgcarr[_xlast]);
302                 return(&(_xlrelarr[_xlast]));
303         }
304
305         /* reuse */
306         res = _xlrelarr[0].moreRecently;
307
308         _xl_remove_hash_entry(&res, 0);
309
310         _xlast--;
311         return(res);
312 }
313
314
315 void
316 XLogInitRelationCache(void)
317 {
318         CreateDummyCaches();
319         _xl_init_rel_cache();
320 }
321
322 void
323 XLogCloseRelationCache(void)
324 {
325
326         DestroyDummyCaches();
327
328         if (!_xlrelarr)
329                 return;
330
331         HashTableWalk(_xlrelcache, (HashtFunc) _xl_remove_hash_entry, 0);
332         hash_destroy(_xlrelcache);
333
334         free(_xlrelarr);
335         free(_xlpgcarr);
336
337         _xlrelarr = NULL;
338 }
339
340 Relation
341 XLogOpenRelation(bool redo, RmgrId rmid, RelFileNode rnode)
342 {
343         XLogRelDesc                        *res;
344         XLogRelCacheEntry          *hentry;
345         bool                                    found;
346
347         hentry = (XLogRelCacheEntry*) 
348                         hash_search(_xlrelcache, (char*)&rnode, HASH_FIND, &found);
349
350         if (hentry == NULL)
351                 elog(STOP, "XLogOpenRelation: error in cache");
352
353         if (found)
354         {
355                 res = hentry->rdesc;
356
357                 res->lessRecently->moreRecently = res->moreRecently;
358                 res->moreRecently->lessRecently = res->lessRecently;
359         }
360         else
361         {
362                 res = _xl_new_reldesc();
363
364                 sprintf(RelationGetPhysicalRelationName(&(res->reldata)), "%u", rnode.relNode);
365
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;
370
371                 hentry = (XLogRelCacheEntry*) 
372                         hash_search(_xlrelcache, (char*)&rnode, HASH_ENTER, &found);
373
374                 if (hentry == NULL)
375                         elog(STOP, "XLogOpenRelation: can't insert into cache");
376
377                 if (found)
378                         elog(STOP, "XLogOpenRelation: file found on insert into cache");
379
380                 hentry->rdesc = res;
381
382                 res->reldata.rd_fd = -1;
383                 res->reldata.rd_fd = smgropen(DEFAULT_SMGR, &(res->reldata),
384                                                                           true /* allow failure */);
385         }
386
387         res->moreRecently = &(_xlrelarr[0]);
388         res->lessRecently = _xlrelarr[0].lessRecently;
389         _xlrelarr[0].lessRecently = res;
390         res->lessRecently->moreRecently = res;
391
392         if (res->reldata.rd_fd < 0)             /* file doesn't exist */
393                 return(NULL);
394
395         return(&(res->reldata));
396 }