Introduce the concept of relation forks. An smgr relation can now consist
[PostgreSQL.git] / src / backend / storage / buffer / localbuf.c
blob7ce77f455570838a1c20026a860068f2aa5b8465
1 /*-------------------------------------------------------------------------
3 * localbuf.c
4 * local buffer manager. Fast buffer manager for temporary tables,
5 * which never need to be WAL-logged or checkpointed, etc.
7 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994-5, Regents of the University of California
11 * IDENTIFICATION
12 * $PostgreSQL$
14 *-------------------------------------------------------------------------
16 #include "postgres.h"
18 #include "storage/buf_internals.h"
19 #include "storage/bufmgr.h"
20 #include "storage/smgr.h"
21 #include "utils/guc.h"
22 #include "utils/memutils.h"
23 #include "utils/resowner.h"
26 /*#define LBDEBUG*/
28 /* entry for buffer lookup hashtable */
29 typedef struct
31 BufferTag key; /* Tag of a disk page */
32 int id; /* Associated local buffer's index */
33 } LocalBufferLookupEnt;
35 /* Note: this macro only works on local buffers, not shared ones! */
36 #define LocalBufHdrGetBlock(bufHdr) \
37 LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)]
39 int NLocBuffer = 0; /* until buffers are initialized */
41 BufferDesc *LocalBufferDescriptors = NULL;
42 Block *LocalBufferBlockPointers = NULL;
43 int32 *LocalRefCount = NULL;
45 static int nextFreeLocalBuf = 0;
47 static HTAB *LocalBufHash = NULL;
50 static void InitLocalBuffers(void);
51 static Block GetLocalBufferStorage(void);
55 * LocalBufferAlloc -
56 * Find or create a local buffer for the given page of the given relation.
58 * API is similar to bufmgr.c's BufferAlloc, except that we do not need
59 * to do any locking since this is all local. Also, IO_IN_PROGRESS
60 * does not get set. Lastly, we support only default access strategy
61 * (hence, usage_count is always advanced).
63 BufferDesc *
64 LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
65 bool *foundPtr)
67 BufferTag newTag; /* identity of requested block */
68 LocalBufferLookupEnt *hresult;
69 BufferDesc *bufHdr;
70 int b;
71 int trycounter;
72 bool found;
74 INIT_BUFFERTAG(newTag, smgr->smgr_rnode, forkNum, blockNum);
76 /* Initialize local buffers if first request in this session */
77 if (LocalBufHash == NULL)
78 InitLocalBuffers();
80 /* See if the desired buffer already exists */
81 hresult = (LocalBufferLookupEnt *)
82 hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL);
84 if (hresult)
86 b = hresult->id;
87 bufHdr = &LocalBufferDescriptors[b];
88 Assert(BUFFERTAGS_EQUAL(bufHdr->tag, newTag));
89 #ifdef LBDEBUG
90 fprintf(stderr, "LB ALLOC (%u,%d) %d\n",
91 smgr->smgr_rnode.relNode, blockNum, -b - 1);
92 #endif
93 /* this part is equivalent to PinBuffer for a shared buffer */
94 if (LocalRefCount[b] == 0)
96 if (bufHdr->usage_count < BM_MAX_USAGE_COUNT)
97 bufHdr->usage_count++;
99 LocalRefCount[b]++;
100 ResourceOwnerRememberBuffer(CurrentResourceOwner,
101 BufferDescriptorGetBuffer(bufHdr));
102 if (bufHdr->flags & BM_VALID)
103 *foundPtr = TRUE;
104 else
106 /* Previous read attempt must have failed; try again */
107 *foundPtr = FALSE;
109 return bufHdr;
112 #ifdef LBDEBUG
113 fprintf(stderr, "LB ALLOC (%u,%d) %d\n",
114 RelationGetRelid(reln), blockNum, -nextFreeLocalBuf - 1);
115 #endif
118 * Need to get a new buffer. We use a clock sweep algorithm (essentially
119 * the same as what freelist.c does now...)
121 trycounter = NLocBuffer;
122 for (;;)
124 b = nextFreeLocalBuf;
126 if (++nextFreeLocalBuf >= NLocBuffer)
127 nextFreeLocalBuf = 0;
129 bufHdr = &LocalBufferDescriptors[b];
131 if (LocalRefCount[b] == 0)
133 if (bufHdr->usage_count > 0)
135 bufHdr->usage_count--;
136 trycounter = NLocBuffer;
138 else
140 /* Found a usable buffer */
141 LocalRefCount[b]++;
142 ResourceOwnerRememberBuffer(CurrentResourceOwner,
143 BufferDescriptorGetBuffer(bufHdr));
144 break;
147 else if (--trycounter == 0)
148 ereport(ERROR,
149 (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
150 errmsg("no empty local buffer available")));
154 * this buffer is not referenced but it might still be dirty. if that's
155 * the case, write it out before reusing it!
157 if (bufHdr->flags & BM_DIRTY)
159 SMgrRelation oreln;
161 /* Find smgr relation for buffer */
162 oreln = smgropen(bufHdr->tag.rnode);
164 /* And write... */
165 smgrwrite(oreln,
166 bufHdr->tag.forkNum,
167 bufHdr->tag.blockNum,
168 (char *) LocalBufHdrGetBlock(bufHdr),
169 true);
171 /* Mark not-dirty now in case we error out below */
172 bufHdr->flags &= ~BM_DIRTY;
174 LocalBufferFlushCount++;
178 * lazy memory allocation: allocate space on first use of a buffer.
180 if (LocalBufHdrGetBlock(bufHdr) == NULL)
182 /* Set pointer for use by BufferGetBlock() macro */
183 LocalBufHdrGetBlock(bufHdr) = GetLocalBufferStorage();
187 * Update the hash table: remove old entry, if any, and make new one.
189 if (bufHdr->flags & BM_TAG_VALID)
191 hresult = (LocalBufferLookupEnt *)
192 hash_search(LocalBufHash, (void *) &bufHdr->tag,
193 HASH_REMOVE, NULL);
194 if (!hresult) /* shouldn't happen */
195 elog(ERROR, "local buffer hash table corrupted");
196 /* mark buffer invalid just in case hash insert fails */
197 CLEAR_BUFFERTAG(bufHdr->tag);
198 bufHdr->flags &= ~(BM_VALID | BM_TAG_VALID);
201 hresult = (LocalBufferLookupEnt *)
202 hash_search(LocalBufHash, (void *) &newTag, HASH_ENTER, &found);
203 if (found) /* shouldn't happen */
204 elog(ERROR, "local buffer hash table corrupted");
205 hresult->id = b;
208 * it's all ours now.
210 bufHdr->tag = newTag;
211 bufHdr->flags &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED | BM_IO_ERROR);
212 bufHdr->flags |= BM_TAG_VALID;
213 bufHdr->usage_count = 1;
215 *foundPtr = FALSE;
216 return bufHdr;
220 * MarkLocalBufferDirty -
221 * mark a local buffer dirty
223 void
224 MarkLocalBufferDirty(Buffer buffer)
226 int bufid;
227 BufferDesc *bufHdr;
229 Assert(BufferIsLocal(buffer));
231 #ifdef LBDEBUG
232 fprintf(stderr, "LB DIRTY %d\n", buffer);
233 #endif
235 bufid = -(buffer + 1);
237 Assert(LocalRefCount[bufid] > 0);
239 bufHdr = &LocalBufferDescriptors[bufid];
240 bufHdr->flags |= BM_DIRTY;
244 * DropRelFileNodeLocalBuffers
245 * This function removes from the buffer pool all the pages of the
246 * specified relation that have block numbers >= firstDelBlock.
247 * (In particular, with firstDelBlock = 0, all pages are removed.)
248 * Dirty pages are simply dropped, without bothering to write them
249 * out first. Therefore, this is NOT rollback-able, and so should be
250 * used only with extreme caution!
252 * See DropRelFileNodeBuffers in bufmgr.c for more notes.
254 void
255 DropRelFileNodeLocalBuffers(RelFileNode rnode, ForkNumber forkNum,
256 BlockNumber firstDelBlock)
258 int i;
260 for (i = 0; i < NLocBuffer; i++)
262 BufferDesc *bufHdr = &LocalBufferDescriptors[i];
263 LocalBufferLookupEnt *hresult;
265 if ((bufHdr->flags & BM_TAG_VALID) &&
266 RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
267 bufHdr->tag.forkNum == forkNum &&
268 bufHdr->tag.blockNum >= firstDelBlock)
270 if (LocalRefCount[i] != 0)
271 elog(ERROR, "block %u of %u/%u/%u is still referenced (local %u)",
272 bufHdr->tag.blockNum,
273 bufHdr->tag.rnode.spcNode,
274 bufHdr->tag.rnode.dbNode,
275 bufHdr->tag.rnode.relNode,
276 LocalRefCount[i]);
277 /* Remove entry from hashtable */
278 hresult = (LocalBufferLookupEnt *)
279 hash_search(LocalBufHash, (void *) &bufHdr->tag,
280 HASH_REMOVE, NULL);
281 if (!hresult) /* shouldn't happen */
282 elog(ERROR, "local buffer hash table corrupted");
283 /* Mark buffer invalid */
284 CLEAR_BUFFERTAG(bufHdr->tag);
285 bufHdr->flags = 0;
286 bufHdr->usage_count = 0;
292 * InitLocalBuffers -
293 * init the local buffer cache. Since most queries (esp. multi-user ones)
294 * don't involve local buffers, we delay allocating actual memory for the
295 * buffers until we need them; just make the buffer headers here.
297 static void
298 InitLocalBuffers(void)
300 int nbufs = num_temp_buffers;
301 HASHCTL info;
302 int i;
304 /* Allocate and zero buffer headers and auxiliary arrays */
305 LocalBufferDescriptors = (BufferDesc *) calloc(nbufs, sizeof(BufferDesc));
306 LocalBufferBlockPointers = (Block *) calloc(nbufs, sizeof(Block));
307 LocalRefCount = (int32 *) calloc(nbufs, sizeof(int32));
308 if (!LocalBufferDescriptors || !LocalBufferBlockPointers || !LocalRefCount)
309 ereport(FATAL,
310 (errcode(ERRCODE_OUT_OF_MEMORY),
311 errmsg("out of memory")));
313 nextFreeLocalBuf = 0;
315 /* initialize fields that need to start off nonzero */
316 for (i = 0; i < nbufs; i++)
318 BufferDesc *buf = &LocalBufferDescriptors[i];
321 * negative to indicate local buffer. This is tricky: shared buffers
322 * start with 0. We have to start with -2. (Note that the routine
323 * BufferDescriptorGetBuffer adds 1 to buf_id so our first buffer id
324 * is -1.)
326 buf->buf_id = -i - 2;
329 /* Create the lookup hash table */
330 MemSet(&info, 0, sizeof(info));
331 info.keysize = sizeof(BufferTag);
332 info.entrysize = sizeof(LocalBufferLookupEnt);
333 info.hash = tag_hash;
335 LocalBufHash = hash_create("Local Buffer Lookup Table",
336 nbufs,
337 &info,
338 HASH_ELEM | HASH_FUNCTION);
340 if (!LocalBufHash)
341 elog(ERROR, "could not initialize local buffer hash table");
343 /* Initialization done, mark buffers allocated */
344 NLocBuffer = nbufs;
348 * GetLocalBufferStorage - allocate memory for a local buffer
350 * The idea of this function is to aggregate our requests for storage
351 * so that the memory manager doesn't see a whole lot of relatively small
352 * requests. Since we'll never give back a local buffer once it's created
353 * within a particular process, no point in burdening memmgr with separately
354 * managed chunks.
356 static Block
357 GetLocalBufferStorage(void)
359 static char *cur_block = NULL;
360 static int next_buf_in_block = 0;
361 static int num_bufs_in_block = 0;
362 static int total_bufs_allocated = 0;
364 char *this_buf;
366 Assert(total_bufs_allocated < NLocBuffer);
368 if (next_buf_in_block >= num_bufs_in_block)
370 /* Need to make a new request to memmgr */
371 int num_bufs;
373 /* Start with a 16-buffer request; subsequent ones double each time */
374 num_bufs = Max(num_bufs_in_block * 2, 16);
375 /* But not more than what we need for all remaining local bufs */
376 num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
377 /* And don't overflow MaxAllocSize, either */
378 num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
380 /* Allocate space from TopMemoryContext so it never goes away */
381 cur_block = (char *) MemoryContextAlloc(TopMemoryContext,
382 num_bufs * BLCKSZ);
383 next_buf_in_block = 0;
384 num_bufs_in_block = num_bufs;
387 /* Allocate next buffer in current memory block */
388 this_buf = cur_block + next_buf_in_block * BLCKSZ;
389 next_buf_in_block++;
390 total_bufs_allocated++;
392 return (Block) this_buf;
396 * AtEOXact_LocalBuffers - clean up at end of transaction.
398 * This is just like AtEOXact_Buffers, but for local buffers.
400 void
401 AtEOXact_LocalBuffers(bool isCommit)
403 #ifdef USE_ASSERT_CHECKING
404 if (assert_enabled)
406 int i;
408 for (i = 0; i < NLocBuffer; i++)
410 Assert(LocalRefCount[i] == 0);
413 #endif
417 * AtProcExit_LocalBuffers - ensure we have dropped pins during backend exit.
419 * This is just like AtProcExit_Buffers, but for local buffers. We have
420 * to drop pins to ensure that any attempt to drop temp files doesn't
421 * fail in DropRelFileNodeBuffers.
423 void
424 AtProcExit_LocalBuffers(void)
426 /* just zero the refcounts ... */
427 if (LocalRefCount)
428 MemSet(LocalRefCount, 0, NLocBuffer * sizeof(*LocalRefCount));