]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/xlogutils.c
Update copyright for 2006. Update scripts.
[postgresql] / src / backend / access / transam / xlogutils.c
1 /*-------------------------------------------------------------------------
2  *
3  * xlogutils.c
4  *
5  * PostgreSQL transaction log manager utility routines
6  *
7  * This file contains support routines that are used by XLOG replay functions.
8  * None of this code is used during normal system operation.
9  *
10  *
11  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
12  * Portions Copyright (c) 1994, Regents of the University of California
13  *
14  * $PostgreSQL: pgsql/src/backend/access/transam/xlogutils.c,v 1.41 2006/03/05 15:58:22 momjian Exp $
15  *
16  *-------------------------------------------------------------------------
17  */
18 #include "postgres.h"
19
20 #include "access/xlogutils.h"
21 #include "storage/bufmgr.h"
22 #include "storage/smgr.h"
23 #include "utils/hsearch.h"
24
25
26 /*
27  *
28  * Storage related support functions
29  *
30  */
31
32 Buffer
33 XLogReadBuffer(bool extend, Relation reln, BlockNumber blkno)
34 {
35         BlockNumber lastblock = RelationGetNumberOfBlocks(reln);
36         Buffer          buffer;
37
38         if (blkno >= lastblock)
39         {
40                 buffer = InvalidBuffer;
41                 if (extend)                             /* we do this in recovery only - no locks */
42                 {
43                         Assert(InRecovery);
44                         while (lastblock <= blkno)
45                         {
46                                 if (buffer != InvalidBuffer)
47                                         ReleaseBuffer(buffer);          /* must be WriteBuffer()? */
48                                 buffer = ReadBuffer(reln, P_NEW);
49                                 lastblock++;
50                         }
51                 }
52                 if (buffer != InvalidBuffer)
53                         LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
54                 return buffer;
55         }
56
57         buffer = ReadBuffer(reln, blkno);
58         if (buffer != InvalidBuffer)
59                 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
60         return buffer;
61 }
62
63
64 /*
65  * Lightweight "Relation" cache --- this substitutes for the normal relcache
66  * during XLOG replay.
67  */
68
69 typedef struct XLogRelDesc
70 {
71         RelationData reldata;
72         struct XLogRelDesc *lessRecently;
73         struct XLogRelDesc *moreRecently;
74 } XLogRelDesc;
75
76 typedef struct XLogRelCacheEntry
77 {
78         RelFileNode rnode;
79         XLogRelDesc *rdesc;
80 } XLogRelCacheEntry;
81
82 static HTAB *_xlrelcache;
83 static XLogRelDesc *_xlrelarr = NULL;
84 static Form_pg_class _xlpgcarr = NULL;
85 static int      _xlast = 0;
86 static int      _xlcnt = 0;
87
88 #define _XLOG_RELCACHESIZE      512
89
90 static void
91 _xl_init_rel_cache(void)
92 {
93         HASHCTL         ctl;
94
95         _xlcnt = _XLOG_RELCACHESIZE;
96         _xlast = 0;
97         _xlrelarr = (XLogRelDesc *) malloc(sizeof(XLogRelDesc) * _xlcnt);
98         memset(_xlrelarr, 0, sizeof(XLogRelDesc) * _xlcnt);
99         _xlpgcarr = (Form_pg_class) malloc(sizeof(FormData_pg_class) * _xlcnt);
100         memset(_xlpgcarr, 0, sizeof(FormData_pg_class) * _xlcnt);
101
102         _xlrelarr[0].moreRecently = &(_xlrelarr[0]);
103         _xlrelarr[0].lessRecently = &(_xlrelarr[0]);
104
105         memset(&ctl, 0, sizeof(ctl));
106         ctl.keysize = sizeof(RelFileNode);
107         ctl.entrysize = sizeof(XLogRelCacheEntry);
108         ctl.hash = tag_hash;
109
110         _xlrelcache = hash_create("XLOG relcache", _XLOG_RELCACHESIZE,
111                                                           &ctl, HASH_ELEM | HASH_FUNCTION);
112 }
113
114 static void
115 _xl_remove_hash_entry(XLogRelDesc *rdesc)
116 {
117         Form_pg_class tpgc = rdesc->reldata.rd_rel;
118         XLogRelCacheEntry *hentry;
119
120         rdesc->lessRecently->moreRecently = rdesc->moreRecently;
121         rdesc->moreRecently->lessRecently = rdesc->lessRecently;
122
123         hentry = (XLogRelCacheEntry *) hash_search(_xlrelcache,
124                                           (void *) &(rdesc->reldata.rd_node), HASH_REMOVE, NULL);
125         if (hentry == NULL)
126                 elog(PANIC, "_xl_remove_hash_entry: file was not found in cache");
127
128         RelationCloseSmgr(&(rdesc->reldata));
129
130         memset(rdesc, 0, sizeof(XLogRelDesc));
131         memset(tpgc, 0, sizeof(FormData_pg_class));
132         rdesc->reldata.rd_rel = tpgc;
133 }
134
135 static XLogRelDesc *
136 _xl_new_reldesc(void)
137 {
138         XLogRelDesc *res;
139
140         _xlast++;
141         if (_xlast < _xlcnt)
142         {
143                 _xlrelarr[_xlast].reldata.rd_rel = &(_xlpgcarr[_xlast]);
144                 return &(_xlrelarr[_xlast]);
145         }
146
147         /* reuse */
148         res = _xlrelarr[0].moreRecently;
149
150         _xl_remove_hash_entry(res);
151
152         _xlast--;
153         return res;
154 }
155
156
157 void
158 XLogInitRelationCache(void)
159 {
160         _xl_init_rel_cache();
161 }
162
163 void
164 XLogCloseRelationCache(void)
165 {
166         HASH_SEQ_STATUS status;
167         XLogRelCacheEntry *hentry;
168
169         if (!_xlrelarr)
170                 return;
171
172         hash_seq_init(&status, _xlrelcache);
173
174         while ((hentry = (XLogRelCacheEntry *) hash_seq_search(&status)) != NULL)
175                 _xl_remove_hash_entry(hentry->rdesc);
176
177         hash_destroy(_xlrelcache);
178
179         free(_xlrelarr);
180         free(_xlpgcarr);
181
182         _xlrelarr = NULL;
183 }
184
185 /*
186  * Open a relation during XLOG replay
187  */
188 Relation
189 XLogOpenRelation(RelFileNode rnode)
190 {
191         XLogRelDesc *res;
192         XLogRelCacheEntry *hentry;
193         bool            found;
194
195         hentry = (XLogRelCacheEntry *)
196                 hash_search(_xlrelcache, (void *) &rnode, HASH_FIND, NULL);
197
198         if (hentry)
199         {
200                 res = hentry->rdesc;
201
202                 res->lessRecently->moreRecently = res->moreRecently;
203                 res->moreRecently->lessRecently = res->lessRecently;
204         }
205         else
206         {
207                 res = _xl_new_reldesc();
208
209                 sprintf(RelationGetRelationName(&(res->reldata)), "%u", rnode.relNode);
210
211                 res->reldata.rd_node = rnode;
212
213                 /*
214                  * We set up the lockRelId in case anything tries to lock the dummy
215                  * relation.  Note that this is fairly bogus since relNode may be
216                  * different from the relation's OID.  It shouldn't really matter
217                  * though, since we are presumably running by ourselves and can't have
218                  * any lock conflicts ...
219                  */
220                 res->reldata.rd_lockInfo.lockRelId.dbId = rnode.dbNode;
221                 res->reldata.rd_lockInfo.lockRelId.relId = rnode.relNode;
222
223                 hentry = (XLogRelCacheEntry *)
224                         hash_search(_xlrelcache, (void *) &rnode, HASH_ENTER, &found);
225
226                 if (found)
227                         elog(PANIC, "XLogOpenRelation: file found on insert into cache");
228
229                 hentry->rdesc = res;
230
231                 res->reldata.rd_targblock = InvalidBlockNumber;
232                 res->reldata.rd_smgr = NULL;
233                 RelationOpenSmgr(&(res->reldata));
234
235                 /*
236                  * Create the target file if it doesn't already exist.  This lets us
237                  * cope if the replay sequence contains writes to a relation that is
238                  * later deleted.  (The original coding of this routine would instead
239                  * return NULL, causing the writes to be suppressed. But that seems
240                  * like it risks losing valuable data if the filesystem loses an inode
241                  * during a crash.      Better to write the data until we are actually
242                  * told to delete the file.)
243                  */
244                 smgrcreate(res->reldata.rd_smgr, res->reldata.rd_istemp, true);
245         }
246
247         res->moreRecently = &(_xlrelarr[0]);
248         res->lessRecently = _xlrelarr[0].lessRecently;
249         _xlrelarr[0].lessRecently = res;
250         res->lessRecently->moreRecently = res;
251
252         return &(res->reldata);
253 }
254
255 /*
256  * Close a relation during XLOG replay
257  *
258  * This is called when the relation is about to be deleted; we need to ensure
259  * that there is no dangling smgr reference in the xlog relation cache.
260  *
261  * Currently, we don't bother to physically remove the relation from the
262  * cache, we just let it age out normally.
263  */
264 void
265 XLogCloseRelation(RelFileNode rnode)
266 {
267         XLogRelDesc *rdesc;
268         XLogRelCacheEntry *hentry;
269
270         hentry = (XLogRelCacheEntry *)
271                 hash_search(_xlrelcache, (void *) &rnode, HASH_FIND, NULL);
272
273         if (!hentry)
274                 return;                                 /* not in cache so no work */
275
276         rdesc = hentry->rdesc;
277
278         RelationCloseSmgr(&(rdesc->reldata));
279 }