Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / src / backend / storage / buffer / localbuf.c
blob641f8e945701cac3d5dfce55ed8234061a09c3d6
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-2009, 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 "catalog/catalog.h"
19 #include "storage/buf_internals.h"
20 #include "storage/bufmgr.h"
21 #include "storage/smgr.h"
22 #include "utils/guc.h"
23 #include "utils/memutils.h"
24 #include "utils/resowner.h"
27 /*#define LBDEBUG*/
29 /* entry for buffer lookup hashtable */
30 typedef struct
32 BufferTag key; /* Tag of a disk page */
33 int id; /* Associated local buffer's index */
34 } LocalBufferLookupEnt;
36 /* Note: this macro only works on local buffers, not shared ones! */
37 #define LocalBufHdrGetBlock(bufHdr) \
38 LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)]
40 int NLocBuffer = 0; /* until buffers are initialized */
42 BufferDesc *LocalBufferDescriptors = NULL;
43 Block *LocalBufferBlockPointers = NULL;
44 int32 *LocalRefCount = NULL;
46 static int nextFreeLocalBuf = 0;
48 static HTAB *LocalBufHash = NULL;
51 static void InitLocalBuffers(void);
52 static Block GetLocalBufferStorage(void);
56 * LocalPrefetchBuffer -
57 * initiate asynchronous read of a block of a relation
59 * Do PrefetchBuffer's work for temporary relations.
60 * No-op if prefetching isn't compiled in.
62 void
63 LocalPrefetchBuffer(SMgrRelation smgr, ForkNumber forkNum,
64 BlockNumber blockNum)
66 #ifdef USE_PREFETCH
67 BufferTag newTag; /* identity of requested block */
68 LocalBufferLookupEnt *hresult;
70 INIT_BUFFERTAG(newTag, smgr->smgr_rnode, forkNum, blockNum);
72 /* Initialize local buffers if first request in this session */
73 if (LocalBufHash == NULL)
74 InitLocalBuffers();
76 /* See if the desired buffer already exists */
77 hresult = (LocalBufferLookupEnt *)
78 hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL);
80 if (hresult)
82 /* Yes, so nothing to do */
83 return;
86 /* Not in buffers, so initiate prefetch */
87 smgrprefetch(smgr, forkNum, blockNum);
88 #endif /* USE_PREFETCH */
93 * LocalBufferAlloc -
94 * Find or create a local buffer for the given page of the given relation.
96 * API is similar to bufmgr.c's BufferAlloc, except that we do not need
97 * to do any locking since this is all local. Also, IO_IN_PROGRESS
98 * does not get set. Lastly, we support only default access strategy
99 * (hence, usage_count is always advanced).
101 BufferDesc *
102 LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
103 bool *foundPtr)
105 BufferTag newTag; /* identity of requested block */
106 LocalBufferLookupEnt *hresult;
107 BufferDesc *bufHdr;
108 int b;
109 int trycounter;
110 bool found;
112 INIT_BUFFERTAG(newTag, smgr->smgr_rnode, forkNum, blockNum);
114 /* Initialize local buffers if first request in this session */
115 if (LocalBufHash == NULL)
116 InitLocalBuffers();
118 /* See if the desired buffer already exists */
119 hresult = (LocalBufferLookupEnt *)
120 hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL);
122 if (hresult)
124 b = hresult->id;
125 bufHdr = &LocalBufferDescriptors[b];
126 Assert(BUFFERTAGS_EQUAL(bufHdr->tag, newTag));
127 #ifdef LBDEBUG
128 fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n",
129 smgr->smgr_rnode.relNode, forkNum, blockNum, -b - 1);
130 #endif
131 /* this part is equivalent to PinBuffer for a shared buffer */
132 if (LocalRefCount[b] == 0)
134 if (bufHdr->usage_count < BM_MAX_USAGE_COUNT)
135 bufHdr->usage_count++;
137 LocalRefCount[b]++;
138 ResourceOwnerRememberBuffer(CurrentResourceOwner,
139 BufferDescriptorGetBuffer(bufHdr));
140 if (bufHdr->flags & BM_VALID)
141 *foundPtr = TRUE;
142 else
144 /* Previous read attempt must have failed; try again */
145 *foundPtr = FALSE;
147 return bufHdr;
150 #ifdef LBDEBUG
151 fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n",
152 smgr->smgr_rnode.relNode, forkNum, blockNum, -nextFreeLocalBuf - 1);
153 #endif
156 * Need to get a new buffer. We use a clock sweep algorithm (essentially
157 * the same as what freelist.c does now...)
159 trycounter = NLocBuffer;
160 for (;;)
162 b = nextFreeLocalBuf;
164 if (++nextFreeLocalBuf >= NLocBuffer)
165 nextFreeLocalBuf = 0;
167 bufHdr = &LocalBufferDescriptors[b];
169 if (LocalRefCount[b] == 0)
171 if (bufHdr->usage_count > 0)
173 bufHdr->usage_count--;
174 trycounter = NLocBuffer;
176 else
178 /* Found a usable buffer */
179 LocalRefCount[b]++;
180 ResourceOwnerRememberBuffer(CurrentResourceOwner,
181 BufferDescriptorGetBuffer(bufHdr));
182 break;
185 else if (--trycounter == 0)
186 ereport(ERROR,
187 (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
188 errmsg("no empty local buffer available")));
192 * this buffer is not referenced but it might still be dirty. if that's
193 * the case, write it out before reusing it!
195 if (bufHdr->flags & BM_DIRTY)
197 SMgrRelation oreln;
199 /* Find smgr relation for buffer */
200 oreln = smgropen(bufHdr->tag.rnode);
202 /* And write... */
203 smgrwrite(oreln,
204 bufHdr->tag.forkNum,
205 bufHdr->tag.blockNum,
206 (char *) LocalBufHdrGetBlock(bufHdr),
207 true);
209 /* Mark not-dirty now in case we error out below */
210 bufHdr->flags &= ~BM_DIRTY;
212 LocalBufferFlushCount++;
216 * lazy memory allocation: allocate space on first use of a buffer.
218 if (LocalBufHdrGetBlock(bufHdr) == NULL)
220 /* Set pointer for use by BufferGetBlock() macro */
221 LocalBufHdrGetBlock(bufHdr) = GetLocalBufferStorage();
225 * Update the hash table: remove old entry, if any, and make new one.
227 if (bufHdr->flags & BM_TAG_VALID)
229 hresult = (LocalBufferLookupEnt *)
230 hash_search(LocalBufHash, (void *) &bufHdr->tag,
231 HASH_REMOVE, NULL);
232 if (!hresult) /* shouldn't happen */
233 elog(ERROR, "local buffer hash table corrupted");
234 /* mark buffer invalid just in case hash insert fails */
235 CLEAR_BUFFERTAG(bufHdr->tag);
236 bufHdr->flags &= ~(BM_VALID | BM_TAG_VALID);
239 hresult = (LocalBufferLookupEnt *)
240 hash_search(LocalBufHash, (void *) &newTag, HASH_ENTER, &found);
241 if (found) /* shouldn't happen */
242 elog(ERROR, "local buffer hash table corrupted");
243 hresult->id = b;
246 * it's all ours now.
248 bufHdr->tag = newTag;
249 bufHdr->flags &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED | BM_IO_ERROR);
250 bufHdr->flags |= BM_TAG_VALID;
251 bufHdr->usage_count = 1;
253 *foundPtr = FALSE;
254 return bufHdr;
258 * MarkLocalBufferDirty -
259 * mark a local buffer dirty
261 void
262 MarkLocalBufferDirty(Buffer buffer)
264 int bufid;
265 BufferDesc *bufHdr;
267 Assert(BufferIsLocal(buffer));
269 #ifdef LBDEBUG
270 fprintf(stderr, "LB DIRTY %d\n", buffer);
271 #endif
273 bufid = -(buffer + 1);
275 Assert(LocalRefCount[bufid] > 0);
277 bufHdr = &LocalBufferDescriptors[bufid];
278 bufHdr->flags |= BM_DIRTY;
282 * DropRelFileNodeLocalBuffers
283 * This function removes from the buffer pool all the pages of the
284 * specified relation that have block numbers >= firstDelBlock.
285 * (In particular, with firstDelBlock = 0, all pages are removed.)
286 * Dirty pages are simply dropped, without bothering to write them
287 * out first. Therefore, this is NOT rollback-able, and so should be
288 * used only with extreme caution!
290 * See DropRelFileNodeBuffers in bufmgr.c for more notes.
292 void
293 DropRelFileNodeLocalBuffers(RelFileNode rnode, ForkNumber forkNum,
294 BlockNumber firstDelBlock)
296 int i;
298 for (i = 0; i < NLocBuffer; i++)
300 BufferDesc *bufHdr = &LocalBufferDescriptors[i];
301 LocalBufferLookupEnt *hresult;
303 if ((bufHdr->flags & BM_TAG_VALID) &&
304 RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
305 bufHdr->tag.forkNum == forkNum &&
306 bufHdr->tag.blockNum >= firstDelBlock)
308 if (LocalRefCount[i] != 0)
309 elog(ERROR, "block %u of %s is still referenced (local %u)",
310 bufHdr->tag.blockNum,
311 relpath(bufHdr->tag.rnode, bufHdr->tag.forkNum),
312 LocalRefCount[i]);
313 /* Remove entry from hashtable */
314 hresult = (LocalBufferLookupEnt *)
315 hash_search(LocalBufHash, (void *) &bufHdr->tag,
316 HASH_REMOVE, NULL);
317 if (!hresult) /* shouldn't happen */
318 elog(ERROR, "local buffer hash table corrupted");
319 /* Mark buffer invalid */
320 CLEAR_BUFFERTAG(bufHdr->tag);
321 bufHdr->flags = 0;
322 bufHdr->usage_count = 0;
328 * InitLocalBuffers -
329 * init the local buffer cache. Since most queries (esp. multi-user ones)
330 * don't involve local buffers, we delay allocating actual memory for the
331 * buffers until we need them; just make the buffer headers here.
333 static void
334 InitLocalBuffers(void)
336 int nbufs = num_temp_buffers;
337 HASHCTL info;
338 int i;
340 /* Allocate and zero buffer headers and auxiliary arrays */
341 LocalBufferDescriptors = (BufferDesc *) calloc(nbufs, sizeof(BufferDesc));
342 LocalBufferBlockPointers = (Block *) calloc(nbufs, sizeof(Block));
343 LocalRefCount = (int32 *) calloc(nbufs, sizeof(int32));
344 if (!LocalBufferDescriptors || !LocalBufferBlockPointers || !LocalRefCount)
345 ereport(FATAL,
346 (errcode(ERRCODE_OUT_OF_MEMORY),
347 errmsg("out of memory")));
349 nextFreeLocalBuf = 0;
351 /* initialize fields that need to start off nonzero */
352 for (i = 0; i < nbufs; i++)
354 BufferDesc *buf = &LocalBufferDescriptors[i];
357 * negative to indicate local buffer. This is tricky: shared buffers
358 * start with 0. We have to start with -2. (Note that the routine
359 * BufferDescriptorGetBuffer adds 1 to buf_id so our first buffer id
360 * is -1.)
362 buf->buf_id = -i - 2;
365 /* Create the lookup hash table */
366 MemSet(&info, 0, sizeof(info));
367 info.keysize = sizeof(BufferTag);
368 info.entrysize = sizeof(LocalBufferLookupEnt);
369 info.hash = tag_hash;
371 LocalBufHash = hash_create("Local Buffer Lookup Table",
372 nbufs,
373 &info,
374 HASH_ELEM | HASH_FUNCTION);
376 if (!LocalBufHash)
377 elog(ERROR, "could not initialize local buffer hash table");
379 /* Initialization done, mark buffers allocated */
380 NLocBuffer = nbufs;
384 * GetLocalBufferStorage - allocate memory for a local buffer
386 * The idea of this function is to aggregate our requests for storage
387 * so that the memory manager doesn't see a whole lot of relatively small
388 * requests. Since we'll never give back a local buffer once it's created
389 * within a particular process, no point in burdening memmgr with separately
390 * managed chunks.
392 static Block
393 GetLocalBufferStorage(void)
395 static char *cur_block = NULL;
396 static int next_buf_in_block = 0;
397 static int num_bufs_in_block = 0;
398 static int total_bufs_allocated = 0;
400 char *this_buf;
402 Assert(total_bufs_allocated < NLocBuffer);
404 if (next_buf_in_block >= num_bufs_in_block)
406 /* Need to make a new request to memmgr */
407 int num_bufs;
409 /* Start with a 16-buffer request; subsequent ones double each time */
410 num_bufs = Max(num_bufs_in_block * 2, 16);
411 /* But not more than what we need for all remaining local bufs */
412 num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
413 /* And don't overflow MaxAllocSize, either */
414 num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
416 /* Allocate space from TopMemoryContext so it never goes away */
417 cur_block = (char *) MemoryContextAlloc(TopMemoryContext,
418 num_bufs * BLCKSZ);
419 next_buf_in_block = 0;
420 num_bufs_in_block = num_bufs;
423 /* Allocate next buffer in current memory block */
424 this_buf = cur_block + next_buf_in_block * BLCKSZ;
425 next_buf_in_block++;
426 total_bufs_allocated++;
428 return (Block) this_buf;
432 * AtEOXact_LocalBuffers - clean up at end of transaction.
434 * This is just like AtEOXact_Buffers, but for local buffers.
436 void
437 AtEOXact_LocalBuffers(bool isCommit)
439 #ifdef USE_ASSERT_CHECKING
440 if (assert_enabled)
442 int i;
444 for (i = 0; i < NLocBuffer; i++)
446 Assert(LocalRefCount[i] == 0);
449 #endif
453 * AtProcExit_LocalBuffers - ensure we have dropped pins during backend exit.
455 * This is just like AtProcExit_Buffers, but for local buffers. We have
456 * to drop pins to ensure that any attempt to drop temp files doesn't
457 * fail in DropRelFileNodeBuffers.
459 void
460 AtProcExit_LocalBuffers(void)
462 /* just zero the refcounts ... */
463 if (LocalRefCount)
464 MemSet(LocalRefCount, 0, NLocBuffer * sizeof(*LocalRefCount));