mirror of
https://github.com/postgres/postgres.git
synced 2025-07-03 20:02:46 +03:00
* Buffer refcount cleanup (per my "progress report" to pghackers, 9/22). * Add links to backend PROC structs to sinval's array of per-backend info, and use these links for routines that need to check the state of all backends (rather than the slow, complicated search of the ShmemIndex hashtable that was used before). Add databaseOID to PROC structs. * Use this to implement an interlock that prevents DESTROY DATABASE of a database containing running backends. (It's a little tricky to prevent a concurrently-starting backend from getting in there, since the new backend is not able to lock anything at the time it tries to look up its database in pg_database. My solution is to recheck that the DB is OK at the end of InitPostgres. It may not be a 100% solution, but it's a lot better than no interlock at all...) * In ALTER TABLE RENAME, flush buffers for the relation before doing the rename of the physical files, to ensure we don't get failures later from mdblindwrt(). * Update TRUNCATE patch so that it actually compiles against current sources :-(. You should do "make clean all" after pulling these changes.
299 lines
6.8 KiB
C
299 lines
6.8 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* localbuf.c
|
|
* local buffer manager. Fast buffer manager for temporary tables
|
|
* or special cases when the operation is not visible to other backends.
|
|
*
|
|
* When a relation is being created, the descriptor will have rd_islocal
|
|
* set to indicate that the local buffer manager should be used. During
|
|
* the same transaction the relation is being created, any inserts or
|
|
* selects from the newly created relation will use the local buffer
|
|
* pool. rd_islocal is reset at the end of a transaction (commit/abort).
|
|
* This is useful for queries like SELECT INTO TABLE and create index.
|
|
*
|
|
* Copyright (c) 1994-5, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/storage/buffer/localbuf.c,v 1.28 1999/09/24 00:24:29 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <sys/file.h>
|
|
#include <math.h>
|
|
#include <signal.h>
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "executor/execdebug.h"
|
|
#include "storage/smgr.h"
|
|
#include "utils/relcache.h"
|
|
|
|
extern long int LocalBufferFlushCount;
|
|
|
|
int NLocBuffer = 64;
|
|
BufferDesc *LocalBufferDescriptors = NULL;
|
|
long *LocalRefCount = NULL;
|
|
|
|
static int nextFreeLocalBuf = 0;
|
|
|
|
/*#define LBDEBUG*/
|
|
|
|
/*
|
|
* LocalBufferAlloc -
|
|
* allocate a local buffer. We do round robin allocation for now.
|
|
*/
|
|
BufferDesc *
|
|
LocalBufferAlloc(Relation reln, BlockNumber blockNum, bool *foundPtr)
|
|
{
|
|
int i;
|
|
BufferDesc *bufHdr = (BufferDesc *) NULL;
|
|
|
|
if (blockNum == P_NEW)
|
|
{
|
|
blockNum = reln->rd_nblocks;
|
|
reln->rd_nblocks++;
|
|
}
|
|
|
|
/* a low tech search for now -- not optimized for scans */
|
|
for (i = 0; i < NLocBuffer; i++)
|
|
{
|
|
if (LocalBufferDescriptors[i].tag.relId.relId == RelationGetRelid(reln) &&
|
|
LocalBufferDescriptors[i].tag.blockNum == blockNum)
|
|
{
|
|
|
|
#ifdef LBDEBUG
|
|
fprintf(stderr, "LB ALLOC (%u,%d) %d\n",
|
|
RelationGetRelid(reln), blockNum, -i - 1);
|
|
#endif
|
|
LocalRefCount[i]++;
|
|
*foundPtr = TRUE;
|
|
return &LocalBufferDescriptors[i];
|
|
}
|
|
}
|
|
|
|
#ifdef LBDEBUG
|
|
fprintf(stderr, "LB ALLOC (%u,%d) %d\n",
|
|
RelationGetRelid(reln), blockNum, -nextFreeLocalBuf - 1);
|
|
#endif
|
|
|
|
/* need to get a new buffer (round robin for now) */
|
|
for (i = 0; i < NLocBuffer; i++)
|
|
{
|
|
int b = (nextFreeLocalBuf + i) % NLocBuffer;
|
|
|
|
if (LocalRefCount[b] == 0)
|
|
{
|
|
bufHdr = &LocalBufferDescriptors[b];
|
|
LocalRefCount[b]++;
|
|
nextFreeLocalBuf = (b + 1) % NLocBuffer;
|
|
break;
|
|
}
|
|
}
|
|
if (bufHdr == NULL)
|
|
elog(ERROR, "no empty local buffer.");
|
|
|
|
/*
|
|
* this buffer is not referenced but it might still be dirty (the last
|
|
* transaction to touch it doesn't need its contents but has not
|
|
* flushed it). if that's the case, write it out before reusing it!
|
|
*/
|
|
if (bufHdr->flags & BM_DIRTY)
|
|
{
|
|
Relation bufrel = RelationIdCacheGetRelation(bufHdr->tag.relId.relId);
|
|
|
|
Assert(bufrel != NULL);
|
|
|
|
/* flush this page */
|
|
smgrwrite(DEFAULT_SMGR, bufrel, bufHdr->tag.blockNum,
|
|
(char *) MAKE_PTR(bufHdr->data));
|
|
LocalBufferFlushCount++;
|
|
|
|
/* drop relcache refcount incremented by RelationIdCacheGetRelation */
|
|
RelationDecrementReferenceCount(bufrel);
|
|
}
|
|
|
|
/*
|
|
* it's all ours now.
|
|
*/
|
|
bufHdr->tag.relId.relId = RelationGetRelid(reln);
|
|
bufHdr->tag.blockNum = blockNum;
|
|
bufHdr->flags &= ~BM_DIRTY;
|
|
|
|
/*
|
|
* lazy memory allocation. (see MAKE_PTR for why we need to do
|
|
* MAKE_OFFSET.)
|
|
*/
|
|
if (bufHdr->data == (SHMEM_OFFSET) 0)
|
|
{
|
|
char *data = (char *) malloc(BLCKSZ);
|
|
|
|
bufHdr->data = MAKE_OFFSET(data);
|
|
}
|
|
|
|
*foundPtr = FALSE;
|
|
return bufHdr;
|
|
}
|
|
|
|
/*
|
|
* WriteLocalBuffer -
|
|
* writes out a local buffer
|
|
*/
|
|
int
|
|
WriteLocalBuffer(Buffer buffer, bool release)
|
|
{
|
|
int bufid;
|
|
|
|
Assert(BufferIsLocal(buffer));
|
|
|
|
#ifdef LBDEBUG
|
|
fprintf(stderr, "LB WRITE %d\n", buffer);
|
|
#endif
|
|
|
|
bufid = -(buffer + 1);
|
|
LocalBufferDescriptors[bufid].flags |= BM_DIRTY;
|
|
|
|
if (release)
|
|
{
|
|
Assert(LocalRefCount[bufid] > 0);
|
|
LocalRefCount[bufid]--;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* FlushLocalBuffer -
|
|
* flushes a local buffer
|
|
*/
|
|
int
|
|
FlushLocalBuffer(Buffer buffer, bool release)
|
|
{
|
|
int bufid;
|
|
Relation bufrel;
|
|
BufferDesc *bufHdr;
|
|
|
|
Assert(BufferIsLocal(buffer));
|
|
|
|
#ifdef LBDEBUG
|
|
fprintf(stderr, "LB FLUSH %d\n", buffer);
|
|
#endif
|
|
|
|
bufid = -(buffer + 1);
|
|
bufHdr = &LocalBufferDescriptors[bufid];
|
|
bufHdr->flags &= ~BM_DIRTY;
|
|
bufrel = RelationIdCacheGetRelation(bufHdr->tag.relId.relId);
|
|
|
|
Assert(bufrel != NULL);
|
|
smgrflush(DEFAULT_SMGR, bufrel, bufHdr->tag.blockNum,
|
|
(char *) MAKE_PTR(bufHdr->data));
|
|
LocalBufferFlushCount++;
|
|
|
|
/* drop relcache refcount incremented by RelationIdCacheGetRelation */
|
|
RelationDecrementReferenceCount(bufrel);
|
|
|
|
if (release)
|
|
{
|
|
Assert(LocalRefCount[bufid] > 0);
|
|
LocalRefCount[bufid]--;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* InitLocalBuffer -
|
|
* init the local buffer cache. Since most queries (esp. multi-user ones)
|
|
* don't involve local buffers, we delay allocating memory for actual the
|
|
* buffer until we need it.
|
|
*/
|
|
void
|
|
InitLocalBuffer(void)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* these aren't going away. I'm not gonna use palloc.
|
|
*/
|
|
LocalBufferDescriptors =
|
|
(BufferDesc *) malloc(sizeof(BufferDesc) * NLocBuffer);
|
|
MemSet(LocalBufferDescriptors, 0, sizeof(BufferDesc) * NLocBuffer);
|
|
nextFreeLocalBuf = 0;
|
|
|
|
for (i = 0; i < NLocBuffer; i++)
|
|
{
|
|
BufferDesc *buf = &LocalBufferDescriptors[i];
|
|
|
|
/*
|
|
* negative to indicate local buffer. This is tricky: shared
|
|
* buffers start with 0. We have to start with -2. (Note that the
|
|
* routine BufferDescriptorGetBuffer adds 1 to buf_id so our first
|
|
* buffer id is -1.)
|
|
*/
|
|
buf->buf_id = -i - 2;
|
|
}
|
|
|
|
LocalRefCount = (long *) malloc(sizeof(long) * NLocBuffer);
|
|
MemSet(LocalRefCount, 0, sizeof(long) * NLocBuffer);
|
|
}
|
|
|
|
/*
|
|
* LocalBufferSync -
|
|
* flush all dirty buffers in the local buffer cache. Since the buffer
|
|
* cache is only used for keeping relations visible during a transaction,
|
|
* we will not need these buffers again.
|
|
*/
|
|
void
|
|
LocalBufferSync(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NLocBuffer; i++)
|
|
{
|
|
BufferDesc *buf = &LocalBufferDescriptors[i];
|
|
Relation bufrel;
|
|
|
|
if (buf->flags & BM_DIRTY)
|
|
{
|
|
#ifdef LBDEBUG
|
|
fprintf(stderr, "LB SYNC %d\n", -i - 1);
|
|
#endif
|
|
bufrel = RelationIdCacheGetRelation(buf->tag.relId.relId);
|
|
|
|
Assert(bufrel != NULL);
|
|
|
|
smgrwrite(DEFAULT_SMGR, bufrel, buf->tag.blockNum,
|
|
(char *) MAKE_PTR(buf->data));
|
|
LocalBufferFlushCount++;
|
|
|
|
/* drop relcache refcount from RelationIdCacheGetRelation */
|
|
RelationDecrementReferenceCount(bufrel);
|
|
|
|
buf->tag.relId.relId = InvalidOid;
|
|
buf->flags &= ~BM_DIRTY;
|
|
}
|
|
}
|
|
|
|
MemSet(LocalRefCount, 0, sizeof(long) * NLocBuffer);
|
|
nextFreeLocalBuf = 0;
|
|
}
|
|
|
|
void
|
|
ResetLocalBufferPool(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NLocBuffer; i++)
|
|
{
|
|
BufferDesc *buf = &LocalBufferDescriptors[i];
|
|
|
|
buf->tag.relId.relId = InvalidOid;
|
|
buf->flags &= ~BM_DIRTY;
|
|
buf->buf_id = -i - 2;
|
|
}
|
|
|
|
MemSet(LocalRefCount, 0, sizeof(long) * NLocBuffer);
|
|
nextFreeLocalBuf = 0;
|
|
}
|