]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/xlogutils.c
WAL
[postgresql] / src / backend / access / transam / xlogutils.c
1 /*-------------------------------------------------------------------------
2  *
3  * xlogutils.c
4  *
5  *
6  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *-------------------------------------------------------------------------
10  */
11
12 #ifdef XLOG
13
14 #include "postgres.h"
15
16 #include "access/xlog.h"
17 #include "access/transam.h"
18 #include "access/xact.h"
19 #include "storage/bufpage.h"
20 #include "storage/bufmgr.h"
21 #include "storage/smgr.h"
22 #include "access/htup.h"
23 #include "access/xlogutils.h"
24 #include "catalog/pg_database.h"
25 #include "lib/hasht.h"
26
27 /*
28  * ---------------------------------------------------------------
29  *
30  * Index support functions
31  *
32  *----------------------------------------------------------------
33  */
34
35 /*
36  * Check if specified heap tuple was inserted by given
37  * xaction/command and return
38  *
39  * - -1 if not
40  * - 0  if there is no tuple at all
41  * - 1  if yes
42  */
43 int
44 XLogIsOwnerOfTuple(RelFileNode hnode, ItemPointer iptr, 
45                                         TransactionId xid, CommandId cid)
46 {
47         Relation                reln;
48         Buffer                  buffer;
49         Page                    page;
50         ItemId                  lp;
51         HeapTupleHeader htup;
52
53         reln = XLogOpenRelation(false, RM_HEAP_ID, hnode);
54         if (!RelationIsValid(reln))
55                 return(0);
56
57         buffer = ReadBuffer(reln, ItemPointerGetBlockNumber(iptr));
58         if (!BufferIsValid(buffer))
59                 return(0);
60
61         LockBuffer(buffer, BUFFER_LOCK_SHARE);
62         page = (Page) BufferGetPage(buffer);
63         if (PageIsNew((PageHeader) page) ||
64                 ItemPointerGetOffsetNumber(iptr) > PageGetMaxOffsetNumber(page))
65         {
66                 UnlockAndReleaseBuffer(buffer);
67                 return(0);
68         }
69         lp = PageGetItemId(page, ItemPointerGetOffsetNumber(iptr));
70         if (!ItemIdIsUsed(lp) || ItemIdDeleted(lp))
71         {
72                 UnlockAndReleaseBuffer(buffer);
73                 return(0);
74         }
75
76         htup = (HeapTupleHeader) PageGetItem(page, lp);
77
78         Assert(PageGetSUI(page) == ThisStartUpID);
79         if (htup->t_xmin != xid || htup->t_cmin != cid)
80         {
81                 UnlockAndReleaseBuffer(buffer);
82                 return(-1);
83         }
84
85         UnlockAndReleaseBuffer(buffer);
86         return(1);
87 }
88
89 /*
90  * MUST BE CALLED ONLY ON RECOVERY.
91  *
92  * Check if exists valid (inserted by not aborted xaction) heap tuple
93  * for given item pointer
94  */
95 bool
96 XLogIsValidTuple(RelFileNode hnode, ItemPointer iptr)
97 {
98         Relation                reln;
99         Buffer                  buffer;
100         Page                    page;
101         ItemId                  lp;
102         HeapTupleHeader htup;
103
104         reln = XLogOpenRelation(false, RM_HEAP_ID, hnode);
105         if (!RelationIsValid(reln))
106                 return(false);
107
108         buffer = ReadBuffer(reln, ItemPointerGetBlockNumber(iptr));
109         if (!BufferIsValid(buffer))
110                 return(false);
111
112         LockBuffer(buffer, BUFFER_LOCK_SHARE);
113         page = (Page) BufferGetPage(buffer);
114         if (PageIsNew((PageHeader) page) ||
115                 ItemPointerGetOffsetNumber(iptr) > PageGetMaxOffsetNumber(page))
116         {
117                 UnlockAndReleaseBuffer(buffer);
118                 return(false);
119         }
120
121         if (PageGetSUI(page) != ThisStartUpID)
122         {
123                 Assert(PageGetSUI(page) < ThisStartUpID);
124                 UnlockAndReleaseBuffer(buffer);
125                 return(true);
126         }
127
128         lp = PageGetItemId(page, ItemPointerGetOffsetNumber(iptr));
129         if (!ItemIdIsUsed(lp) || ItemIdDeleted(lp))
130         {
131                 UnlockAndReleaseBuffer(buffer);
132                 return(false);
133         }
134
135         htup = (HeapTupleHeader) PageGetItem(page, lp);
136
137         /* MUST CHECK WASN'T TUPLE INSERTED IN PREV STARTUP */
138
139         if (!(htup->t_infomask & HEAP_XMIN_COMMITTED))
140         {
141                 if (htup->t_infomask & HEAP_XMIN_INVALID ||
142                         (htup->t_infomask & HEAP_MOVED_IN &&
143                         TransactionIdDidAbort((TransactionId)htup->t_cmin)) ||
144                         TransactionIdDidAbort(htup->t_xmin))
145                 {
146                         UnlockAndReleaseBuffer(buffer);
147                         return(false);
148                 }
149         }
150
151         UnlockAndReleaseBuffer(buffer);
152         return(true);
153 }
154
155 /*
156  * Open pg_log in recovery
157  */
158 extern Relation LogRelation;    /* pg_log relation */
159
160 void
161 XLogOpenLogRelation(void)
162 {
163         Relation        logRelation;
164
165         Assert(!LogRelation);
166         logRelation = (Relation) malloc(sizeof(RelationData));
167         memset(logRelation, 0, sizeof(RelationData));
168         logRelation->rd_rel = (Form_pg_class) malloc(sizeof(FormData_pg_class));
169         memset(logRelation->rd_rel, 0, sizeof(FormData_pg_class));
170
171         sprintf(RelationGetPhysicalRelationName(logRelation), "pg_log");
172         logRelation->rd_node.tblNode = InvalidOid;
173         logRelation->rd_node.relNode = RelOid_pg_log;
174         logRelation->rd_unlinked = false;       /* must exists */
175         logRelation->rd_fd = -1;
176         logRelation->rd_fd = smgropen(DEFAULT_SMGR, logRelation);
177         if (logRelation->rd_fd < 0)
178                 elog(STOP, "XLogOpenLogRelation: failed to open pg_log");
179         LogRelation = logRelation;
180 }
181
182 /*
183  * ---------------------------------------------------------------
184  *
185  * Storage related support functions
186  *
187  *----------------------------------------------------------------
188  */
189
190 Buffer
191 XLogReadBuffer(bool extend, Relation reln, BlockNumber blkno)
192 {
193         BlockNumber     lastblock = RelationGetNumberOfBlocks(reln);
194         Buffer          buffer;
195
196         if (blkno >= lastblock)
197         {
198                 buffer = InvalidBuffer;
199                 if (extend)             /* we do this in recovery only - no locks */
200                 {
201                         Assert(InRecovery);
202                         while (lastblock <= blkno)
203                         {
204                                 buffer = ReadBuffer(reln, P_NEW);
205                                 lastblock++;
206                         }
207                 }
208                 if (buffer != InvalidBuffer)
209                         LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
210                 return(buffer);
211         }
212
213         buffer = ReadBuffer(reln, blkno);
214         if (buffer != InvalidBuffer)
215                 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
216         return(buffer);
217 }
218
219 /*
220  * "Relation" cache
221  */
222
223 typedef struct XLogRelDesc
224 {
225         RelationData                    reldata;
226         struct XLogRelDesc         *lessRecently;
227         struct XLogRelDesc         *moreRecently;
228 } XLogRelDesc;
229
230 typedef struct XLogRelCacheEntry
231 {
232         RelFileNode             rnode;
233         XLogRelDesc        *rdesc;
234 } XLogRelCacheEntry;
235
236 static HTAB                                *_xlrelcache;
237 static XLogRelDesc                 *_xlrelarr = NULL;
238 static Form_pg_class            _xlpgcarr = NULL;
239 static int                                      _xlast = 0;
240 static int                                      _xlcnt = 0;
241 #define _XLOG_INITRELCACHESIZE  32
242 #define _XLOG_MAXRELCACHESIZE   512
243
244 static void
245 _xl_init_rel_cache(void)
246 {
247         HASHCTL                 ctl;
248
249         _xlcnt = _XLOG_INITRELCACHESIZE;
250         _xlast = 0;
251         _xlrelarr = (XLogRelDesc*) malloc(sizeof(XLogRelDesc) * _xlcnt);
252         memset(_xlrelarr, 0, sizeof(XLogRelDesc) * _xlcnt);
253         _xlpgcarr = (Form_pg_class) malloc(sizeof(FormData_pg_class) * _xlcnt);
254         memset(_xlpgcarr, 0, sizeof(FormData_pg_class) * _xlcnt);
255
256         _xlrelarr[0].moreRecently = &(_xlrelarr[0]);
257         _xlrelarr[0].lessRecently = &(_xlrelarr[0]);
258
259         memset(&ctl, 0, (int) sizeof(ctl));
260         ctl.keysize = sizeof(RelFileNode);
261         ctl.datasize = sizeof(XLogRelDesc*);
262         ctl.hash = tag_hash;
263
264         _xlrelcache = hash_create(_XLOG_INITRELCACHESIZE, &ctl,
265                                                                 HASH_ELEM | HASH_FUNCTION);
266 }
267
268 static void
269 _xl_remove_hash_entry(XLogRelDesc **edata, int dummy)
270 {
271         XLogRelCacheEntry          *hentry;
272         bool                                    found;
273         XLogRelDesc                        *rdesc = *edata;
274         Form_pg_class                   tpgc = rdesc->reldata.rd_rel;
275
276         rdesc->lessRecently->moreRecently = rdesc->moreRecently;
277         rdesc->moreRecently->lessRecently = rdesc->lessRecently;
278
279         hentry = (XLogRelCacheEntry*) hash_search(_xlrelcache, 
280                 (char*)&(rdesc->reldata.rd_node), HASH_REMOVE, &found);
281
282         if (hentry == NULL)
283                 elog(STOP, "_xl_remove_hash_entry: can't delete from cache");
284         if (!found)
285                 elog(STOP, "_xl_remove_hash_entry: file was not found in cache");
286
287         if (rdesc->reldata.rd_fd >= 0)
288                 smgrclose(DEFAULT_SMGR, &(rdesc->reldata));
289
290         memset(rdesc, 0, sizeof(XLogRelDesc));
291         memset(tpgc, 0, sizeof(FormData_pg_class));
292         rdesc->reldata.rd_rel = tpgc;
293
294         return;
295 }
296
297 static XLogRelDesc*
298 _xl_new_reldesc(void)
299 {
300         _xlast++;
301         if (_xlast < _xlcnt)
302         {
303                 _xlrelarr[_xlast].reldata.rd_rel = &(_xlpgcarr[_xlast]);
304                 return(&(_xlrelarr[_xlast]));
305         }
306
307         if ( 2 * _xlcnt <= _XLOG_MAXRELCACHESIZE)
308         {
309                 _xlrelarr = (XLogRelDesc*) realloc(_xlrelarr, 
310                                                 2 * sizeof(XLogRelDesc) * _xlcnt);
311                 memset(&(_xlrelarr[_xlcnt]), 0, sizeof(XLogRelDesc) * _xlcnt);
312                 _xlpgcarr = (Form_pg_class) realloc(_xlpgcarr, 
313                                                 2 * sizeof(FormData_pg_class) * _xlcnt);
314                 memset(&(_xlpgcarr[_xlcnt]), 0, sizeof(FormData_pg_class) * _xlcnt);
315                 _xlcnt += _xlcnt;
316                 _xlrelarr[_xlast].reldata.rd_rel = &(_xlpgcarr[_xlast]);
317                 return(&(_xlrelarr[_xlast]));
318         }
319         else /* reuse */
320         {
321                 XLogRelDesc        *res = _xlrelarr[0].moreRecently;
322
323                 _xl_remove_hash_entry(&res, 0);
324
325                 _xlast--;
326                 return(res);
327         }
328 }
329
330 extern void CreateDummyCaches(void);
331 extern void DestroyDummyCaches(void);
332
333 void
334 XLogInitRelationCache(void)
335 {
336         CreateDummyCaches();
337         _xl_init_rel_cache();
338 }
339
340 void
341 XLogCloseRelationCache(void)
342 {
343
344         DestroyDummyCaches();
345
346         if (!_xlrelarr)
347                 return;
348
349         HashTableWalk(_xlrelcache, (HashtFunc)_xl_remove_hash_entry, 0);
350         hash_destroy(_xlrelcache);
351
352         free(_xlrelarr);
353         free(_xlpgcarr);
354
355         _xlrelarr = NULL;
356 }
357
358 Relation
359 XLogOpenRelation(bool redo, RmgrId rmid, RelFileNode rnode)
360 {
361         XLogRelDesc                        *res;
362         XLogRelCacheEntry          *hentry;
363         bool                                    found;
364
365         hentry = (XLogRelCacheEntry*) 
366                         hash_search(_xlrelcache, (char*)&rnode, HASH_FIND, &found);
367
368         if (hentry == NULL)
369                 elog(STOP, "XLogOpenRelation: error in cache");
370
371         if (found)
372         {
373                 res = hentry->rdesc;
374
375                 res->lessRecently->moreRecently = res->moreRecently;
376                 res->moreRecently->lessRecently = res->lessRecently;
377         }
378         else
379         {
380                 res = _xl_new_reldesc();
381
382                 sprintf(RelationGetPhysicalRelationName(&(res->reldata)), "%u", rnode.relNode);
383
384                 /* unexisting DB id */
385                 res->reldata.rd_lockInfo.lockRelId.dbId = RecoveryDb;
386                 res->reldata.rd_lockInfo.lockRelId.relId = rnode.relNode;
387                 res->reldata.rd_node = rnode;
388
389                 hentry = (XLogRelCacheEntry*) 
390                         hash_search(_xlrelcache, (char*)&rnode, HASH_ENTER, &found);
391
392                 if (hentry == NULL)
393                         elog(STOP, "XLogOpenRelation: can't insert into cache");
394
395                 if (found)
396                         elog(STOP, "XLogOpenRelation: file found on insert into cache");
397
398                 hentry->rdesc = res;
399
400                 res->reldata.rd_unlinked = true;        /* look smgropen */
401                 res->reldata.rd_fd = -1;
402                 res->reldata.rd_fd = smgropen(DEFAULT_SMGR, &(res->reldata));
403         }
404
405         res->moreRecently = &(_xlrelarr[0]);
406         res->lessRecently = _xlrelarr[0].lessRecently;
407         _xlrelarr[0].lessRecently = res;
408         res->lessRecently->moreRecently = res;
409
410         if (res->reldata.rd_fd < 0)             /* file doesn't exist */
411                 return(NULL);
412
413         return(&(res->reldata));
414 }
415
416 #endif