OSDN Git Service

Update copyright to 2004.
[pg-rex/syncrep.git] / src / backend / storage / buffer / localbuf.c
index 480dee7..703bd9a 100644 (file)
 /*-------------------------------------------------------------------------
  *
- * localbuf.c--
- *       local buffer manager. Fast buffer manager for temporary tables
- *       or special cases when the operation is not visible to other backends.
+ * localbuf.c
+ *       local buffer manager. Fast buffer manager for temporary tables,
+ *       which never need to be WAL-logged or checkpointed, etc.
  *
- *       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
+ * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/storage/buffer/localbuf.c,v 1.11 1997/09/08 21:46:52 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/storage/buffer/localbuf.c,v 1.58 2004/08/29 04:12:47 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
-#include <sys/types.h>
-#include <sys/file.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-#include <signal.h>
-
 #include "postgres.h"
 
-/* declarations split between these three files */
-#include "storage/buf.h"
 #include "storage/buf_internals.h"
 #include "storage/bufmgr.h"
-
-#include "storage/fd.h"
-#include "storage/ipc.h"
-#include "storage/shmem.h"
-#include "storage/spin.h"
 #include "storage/smgr.h"
-#include "storage/lmgr.h"
-#include "storage/buf_internals.h"
-#include "miscadmin.h"
-#include "utils/builtins.h"
-#include "utils/hsearch.h"
-#include "utils/memutils.h"
 #include "utils/relcache.h"
-#include "executor/execdebug.h" /* for NDirectFileRead */
-#include "catalog/catalog.h"
+#include "utils/resowner.h"
+
 
-extern long int LocalBufferFlushCount;
+/*#define LBDEBUG*/
 
+/* should be a GUC parameter some day */
 int                    NLocBuffer = 64;
+
 BufferDesc *LocalBufferDescriptors = NULL;
-long      *LocalRefCount = NULL;
+Block     *LocalBufferBlockPointers = NULL;
+int32     *LocalRefCount = NULL;
 
 static int     nextFreeLocalBuf = 0;
 
-/*#define LBDEBUG*/
 
 /*
  * LocalBufferAlloc -
  *       allocate a local buffer. We do round robin allocation for now.
+ *
+ * API is similar to bufmgr.c's BufferAlloc, except that we do not need
+ * to have the BufMgrLock since this is all local.  Also, IO_IN_PROGRESS
+ * does not get set.
  */
 BufferDesc *
 LocalBufferAlloc(Relation reln, BlockNumber blockNum, bool *foundPtr)
 {
+       BufferTag       newTag;                 /* identity of requested block */
        int                     i;
-       BufferDesc *bufHdr = (BufferDesc *) NULL;
+       BufferDesc *bufHdr;
 
-       if (blockNum == P_NEW)
-       {
-               blockNum = reln->rd_nblocks;
-               reln->rd_nblocks++;
-       }
+       INIT_BUFFERTAG(newTag, reln, blockNum);
 
        /* a low tech search for now -- not optimized for scans */
        for (i = 0; i < NLocBuffer; i++)
        {
-               if (LocalBufferDescriptors[i].tag.relId.relId == reln->rd_id &&
-                       LocalBufferDescriptors[i].tag.blockNum == blockNum)
+               bufHdr = &LocalBufferDescriptors[i];
+               if (BUFFERTAGS_EQUAL(bufHdr->tag, newTag))
                {
-
 #ifdef LBDEBUG
-                       fprintf(stderr, "LB ALLOC (%d,%d) %d\n",
-                                       reln->rd_id, blockNum, -i - 1);
+                       fprintf(stderr, "LB ALLOC (%u,%d) %d\n",
+                                       RelationGetRelid(reln), blockNum, -i - 1);
 #endif
+
                        LocalRefCount[i]++;
-                       *foundPtr = TRUE;
-                       return &LocalBufferDescriptors[i];
+                       ResourceOwnerRememberBuffer(CurrentResourceOwner,
+                                                                               BufferDescriptorGetBuffer(bufHdr));
+                       if (bufHdr->flags & BM_VALID)
+                               *foundPtr = TRUE;
+                       else
+                       {
+                               /* Previous read attempt must have failed; try again */
+                               *foundPtr = FALSE;
+                       }
+                       return bufHdr;
                }
        }
 
 #ifdef LBDEBUG
-       fprintf(stderr, "LB ALLOC (%d,%d) %d\n",
-                       reln->rd_id, blockNum, -nextFreeLocalBuf - 1);
+       fprintf(stderr, "LB ALLOC (%u,%d) %d\n",
+                       RelationGetRelid(reln), blockNum, -nextFreeLocalBuf - 1);
 #endif
 
        /* need to get a new buffer (round robin for now) */
+       bufHdr = NULL;
        for (i = 0; i < NLocBuffer; i++)
        {
                int                     b = (nextFreeLocalBuf + i) % NLocBuffer;
@@ -105,57 +91,82 @@ LocalBufferAlloc(Relation reln, BlockNumber blockNum, bool *foundPtr)
                {
                        bufHdr = &LocalBufferDescriptors[b];
                        LocalRefCount[b]++;
+                       ResourceOwnerRememberBuffer(CurrentResourceOwner,
+                                                                               BufferDescriptorGetBuffer(bufHdr));
                        nextFreeLocalBuf = (b + 1) % NLocBuffer;
                        break;
                }
        }
        if (bufHdr == NULL)
-               elog(WARN, "no empty local buffer.");
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+                                errmsg("no empty local buffer available")));
 
        /*
-        * 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!
+        * this buffer is not referenced but it might still be dirty. if
+        * that's the case, write it out before reusing it!
         */
-       if (bufHdr->flags & BM_DIRTY)
+       if (bufHdr->flags & BM_DIRTY || bufHdr->cntxDirty)
        {
-               Relation        bufrel = RelationIdCacheGetRelation(bufHdr->tag.relId.relId);
+               SMgrRelation reln;
+
+               /* Find smgr relation for buffer */
+               reln = smgropen(bufHdr->tag.rnode);
 
-               Assert(bufrel != NULL);
+               /* And write... */
+               smgrwrite(reln,
+                                 bufHdr->tag.blockNum,
+                                 (char *) MAKE_PTR(bufHdr->data),
+                                 true);
 
-               /* flush this page */
-               smgrwrite(bufrel->rd_rel->relsmgr, bufrel, bufHdr->tag.blockNum,
-                                 (char *) MAKE_PTR(bufHdr->data));
                LocalBufferFlushCount++;
        }
 
        /*
-        * it's all ours now.
-        */
-       bufHdr->tag.relId.relId = reln->rd_id;
-       bufHdr->tag.blockNum = blockNum;
-       bufHdr->flags &= ~BM_DIRTY;
-
-       /*
-        * lazy memory allocation. (see MAKE_PTR for why we need to do
-        * MAKE_OFFSET.)
+        * lazy memory allocation: allocate space on first use of a buffer.
+        *
+        * Note this path cannot be taken for a buffer that was previously in
+        * use, so it's okay to do it (and possibly error out) before marking
+        * the buffer as not dirty.
         */
        if (bufHdr->data == (SHMEM_OFFSET) 0)
        {
                char       *data = (char *) malloc(BLCKSZ);
 
+               if (data == NULL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_OUT_OF_MEMORY),
+                                        errmsg("out of memory")));
+
+               /*
+                * This is a bit of a hack: bufHdr->data needs to be a shmem
+                * offset for consistency with the shared-buffer case, so make it
+                * one even though it's not really a valid shmem offset.
+                */
                bufHdr->data = MAKE_OFFSET(data);
+
+               /*
+                * Set pointer for use by BufferGetBlock() macro.
+                */
+               LocalBufferBlockPointers[-(bufHdr->buf_id + 2)] = (Block) data;
        }
 
+       /*
+        * it's all ours now.
+        */
+       bufHdr->tag = newTag;
+       bufHdr->flags &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED | BM_IO_ERROR);
+       bufHdr->cntxDirty = false;
+
        *foundPtr = FALSE;
        return bufHdr;
 }
 
 /*
  * WriteLocalBuffer -
- *       writes out a local buffer
+ *       writes out a local buffer (actually, just marks it dirty)
  */
-int
+void
 WriteLocalBuffer(Buffer buffer, bool release)
 {
        int                     bufid;
@@ -173,50 +184,15 @@ WriteLocalBuffer(Buffer buffer, bool release)
        {
                Assert(LocalRefCount[bufid] > 0);
                LocalRefCount[bufid]--;
+               ResourceOwnerForgetBuffer(CurrentResourceOwner, buffer);
        }
-
-       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(bufrel->rd_rel->relsmgr, bufrel, bufHdr->tag.blockNum,
-                         (char *) MAKE_PTR(bufHdr->data));
-       LocalBufferFlushCount++;
-
-       Assert(LocalRefCount[bufid] > 0);
-       if (release)
-               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.
+ *       don't involve local buffers, we delay allocating actual memory for the
+ *       buffers until we need them; just make the buffer headers here.
  */
 void
 InitLocalBuffer(void)
@@ -226,9 +202,12 @@ InitLocalBuffer(void)
        /*
         * these aren't going away. I'm not gonna use palloc.
         */
-       LocalBufferDescriptors =
-               (BufferDesc *) malloc(sizeof(BufferDesc) * NLocBuffer);
-       memset(LocalBufferDescriptors, 0, sizeof(BufferDesc) * NLocBuffer);
+       LocalBufferDescriptors = (BufferDesc *)
+               calloc(NLocBuffer, sizeof(*LocalBufferDescriptors));
+       LocalBufferBlockPointers = (Block *)
+               calloc(NLocBuffer, sizeof(*LocalBufferBlockPointers));
+       LocalRefCount = (int32 *)
+               calloc(NLocBuffer, sizeof(*LocalRefCount));
        nextFreeLocalBuf = 0;
 
        for (i = 0; i < NLocBuffer; i++)
@@ -243,64 +222,33 @@ InitLocalBuffer(void)
                 */
                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.
+ * AtEOXact_LocalBuffers - clean up at end of transaction.
+ *
+ * This is just like AtEOXact_Buffers, but for local buffers.
  */
 void
-LocalBufferSync(void)
+AtEOXact_LocalBuffers(bool isCommit)
 {
        int                     i;
 
        for (i = 0; i < NLocBuffer; i++)
        {
-               BufferDesc *buf = &LocalBufferDescriptors[i];
-               Relation        bufrel;
-
-               if (buf->flags & BM_DIRTY)
+               if (LocalRefCount[i] != 0)
                {
-#ifdef LBDEBUG
-                       fprintf(stderr, "LB SYNC %d\n", -i - 1);
-#endif
-                       bufrel = RelationIdCacheGetRelation(buf->tag.relId.relId);
-
-                       Assert(bufrel != NULL);
+                       BufferDesc *buf = &(LocalBufferDescriptors[i]);
 
-                       smgrwrite(bufrel->rd_rel->relsmgr, bufrel, buf->tag.blockNum,
-                                         (char *) MAKE_PTR(buf->data));
-                       LocalBufferFlushCount++;
+                       if (isCommit)
+                               elog(WARNING,
+                                        "local buffer leak: [%03d] (rel=%u/%u/%u, blockNum=%u, flags=0x%x, refcount=%u %d)",
+                                        i,
+                                        buf->tag.rnode.spcNode, buf->tag.rnode.dbNode,
+                                        buf->tag.rnode.relNode, buf->tag.blockNum, buf->flags,
+                                        buf->refcount, LocalRefCount[i]);
 
-                       buf->tag.relId.relId = InvalidOid;
-                       buf->flags &= ~BM_DIRTY;
+                       LocalRefCount[i] = 0;
                }
        }
-
-       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;
 }