595 lines
12 KiB
C
595 lines
12 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* mm.c--
|
|
* main memory storage manager
|
|
*
|
|
* This code manages relations that reside in (presumably stable)
|
|
* main memory.
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/storage/smgr/Attic/mm.c,v 1.12.2.1 1999/03/07 02:00:52 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#ifdef STABLE_MEMORY_STORAGE
|
|
|
|
#include <math.h>
|
|
#include "storage/ipc.h"
|
|
#include "storage/smgr.h" /* where the declarations go */
|
|
#include "storage/block.h"
|
|
#include "storage/shmem.h"
|
|
#include "storage/spin.h"
|
|
|
|
#include "utils/hsearch.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/memutils.h"
|
|
|
|
/*
|
|
* MMCacheTag -- Unique triplet for blocks stored by the main memory
|
|
* storage manager.
|
|
*/
|
|
|
|
typedef struct MMCacheTag
|
|
{
|
|
Oid mmct_dbid;
|
|
Oid mmct_relid;
|
|
BlockNumber mmct_blkno;
|
|
} MMCacheTag;
|
|
|
|
/*
|
|
* Shared-memory hash table for main memory relations contains
|
|
* entries of this form.
|
|
*/
|
|
|
|
typedef struct MMHashEntry
|
|
{
|
|
MMCacheTag mmhe_tag;
|
|
int mmhe_bufno;
|
|
} MMHashEntry;
|
|
|
|
/*
|
|
* MMRelTag -- Unique identifier for each relation that is stored in the
|
|
* main-memory storage manager.
|
|
*/
|
|
|
|
typedef struct MMRelTag
|
|
{
|
|
Oid mmrt_dbid;
|
|
Oid mmrt_relid;
|
|
} MMRelTag;
|
|
|
|
/*
|
|
* Shared-memory hash table for # blocks in main memory relations contains
|
|
* entries of this form.
|
|
*/
|
|
|
|
typedef struct MMRelHashEntry
|
|
{
|
|
MMRelTag mmrhe_tag;
|
|
int mmrhe_nblocks;
|
|
} MMRelHashEntry;
|
|
|
|
#define MMNBUFFERS 10
|
|
#define MMNRELATIONS 2
|
|
|
|
SPINLOCK MMCacheLock;
|
|
extern Oid MyDatabaseId;
|
|
|
|
static int *MMCurTop;
|
|
static int *MMCurRelno;
|
|
static MMCacheTag *MMBlockTags;
|
|
static char *MMBlockCache;
|
|
static HTAB *MMCacheHT;
|
|
static HTAB *MMRelCacheHT;
|
|
|
|
int
|
|
mminit()
|
|
{
|
|
char *mmcacheblk;
|
|
int mmsize = 0;
|
|
bool found;
|
|
HASHCTL info;
|
|
|
|
SpinAcquire(MMCacheLock);
|
|
|
|
mmsize += MAXALIGN(BLCKSZ * MMNBUFFERS);
|
|
mmsize += MAXALIGN(sizeof(*MMCurTop));
|
|
mmsize += MAXALIGN(sizeof(*MMCurRelno));
|
|
mmsize += MAXALIGN((MMNBUFFERS * sizeof(MMCacheTag)));
|
|
mmcacheblk = (char *) ShmemInitStruct("Main memory smgr", mmsize, &found);
|
|
|
|
if (mmcacheblk == (char *) NULL)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
return SM_FAIL;
|
|
}
|
|
|
|
info.keysize = sizeof(MMCacheTag);
|
|
info.datasize = sizeof(MMHashEntry) - sizeof(MMCacheTag);
|
|
info.hash = tag_hash;
|
|
|
|
MMCacheHT = (HTAB *) ShmemInitHash("Main memory store HT",
|
|
MMNBUFFERS, MMNBUFFERS,
|
|
&info, (HASH_ELEM | HASH_FUNCTION));
|
|
|
|
if (MMCacheHT == (HTAB *) NULL)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
return SM_FAIL;
|
|
}
|
|
|
|
info.keysize = sizeof(MMRelTag);
|
|
info.datasize = sizeof(MMRelHashEntry) - sizeof(MMRelTag);
|
|
info.hash = tag_hash;
|
|
|
|
MMRelCacheHT = (HTAB *) ShmemInitHash("Main memory rel HT",
|
|
MMNRELATIONS, MMNRELATIONS,
|
|
&info, (HASH_ELEM | HASH_FUNCTION));
|
|
|
|
if (MMRelCacheHT == (HTAB *) NULL)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
return SM_FAIL;
|
|
}
|
|
|
|
if (IsUnderPostmaster) /* was IsPostmaster bjm */
|
|
{
|
|
MemSet(mmcacheblk, 0, mmsize);
|
|
SpinRelease(MMCacheLock);
|
|
return SM_SUCCESS;
|
|
}
|
|
|
|
SpinRelease(MMCacheLock);
|
|
|
|
MMCurTop = (int *) mmcacheblk;
|
|
mmcacheblk += sizeof(int);
|
|
MMCurRelno = (int *) mmcacheblk;
|
|
mmcacheblk += sizeof(int);
|
|
MMBlockTags = (MMCacheTag *) mmcacheblk;
|
|
mmcacheblk += (MMNBUFFERS * sizeof(MMCacheTag));
|
|
MMBlockCache = mmcacheblk;
|
|
|
|
return SM_SUCCESS;
|
|
}
|
|
|
|
int
|
|
mmshutdown()
|
|
{
|
|
return SM_SUCCESS;
|
|
}
|
|
|
|
int
|
|
mmcreate(Relation reln)
|
|
{
|
|
MMRelHashEntry *entry;
|
|
bool found;
|
|
MMRelTag tag;
|
|
|
|
SpinAcquire(MMCacheLock);
|
|
|
|
if (*MMCurRelno == MMNRELATIONS)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
return SM_FAIL;
|
|
}
|
|
|
|
(*MMCurRelno)++;
|
|
|
|
tag.mmrt_relid = RelationGetRelid(reln);
|
|
if (reln->rd_rel->relisshared)
|
|
tag.mmrt_dbid = (Oid) 0;
|
|
else
|
|
tag.mmrt_dbid = MyDatabaseId;
|
|
|
|
entry = (MMRelHashEntry *) hash_search(MMRelCacheHT,
|
|
(char *) &tag, HASH_ENTER, &found);
|
|
|
|
if (entry == (MMRelHashEntry *) NULL)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
elog(FATAL, "main memory storage mgr rel cache hash table corrupt");
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
/* already exists */
|
|
SpinRelease(MMCacheLock);
|
|
return SM_FAIL;
|
|
}
|
|
|
|
entry->mmrhe_nblocks = 0;
|
|
|
|
SpinRelease(MMCacheLock);
|
|
|
|
return SM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* mmunlink() -- Unlink a relation.
|
|
*/
|
|
int
|
|
mmunlink(Relation reln)
|
|
{
|
|
int i;
|
|
Oid reldbid;
|
|
MMHashEntry *entry;
|
|
MMRelHashEntry *rentry;
|
|
bool found;
|
|
MMRelTag rtag;
|
|
|
|
if (reln->rd_rel->relisshared)
|
|
reldbid = (Oid) 0;
|
|
else
|
|
reldbid = MyDatabaseId;
|
|
|
|
SpinAcquire(MMCacheLock);
|
|
|
|
for (i = 0; i < MMNBUFFERS; i++)
|
|
{
|
|
if (MMBlockTags[i].mmct_dbid == reldbid
|
|
&& MMBlockTags[i].mmct_relid == RelationGetRelid(reln))
|
|
{
|
|
entry = (MMHashEntry *) hash_search(MMCacheHT,
|
|
(char *) &MMBlockTags[i],
|
|
HASH_REMOVE, &found);
|
|
if (entry == (MMHashEntry *) NULL || !found)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
elog(FATAL, "mmunlink: cache hash table corrupted");
|
|
}
|
|
MMBlockTags[i].mmct_dbid = (Oid) 0;
|
|
MMBlockTags[i].mmct_relid = (Oid) 0;
|
|
MMBlockTags[i].mmct_blkno = (BlockNumber) 0;
|
|
}
|
|
}
|
|
rtag.mmrt_dbid = reldbid;
|
|
rtag.mmrt_relid = RelationGetRelid(reln);
|
|
|
|
rentry = (MMRelHashEntry *) hash_search(MMRelCacheHT, (char *) &rtag,
|
|
HASH_REMOVE, &found);
|
|
|
|
if (rentry == (MMRelHashEntry *) NULL || !found)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
elog(FATAL, "mmunlink: rel cache hash table corrupted");
|
|
}
|
|
|
|
(*MMCurRelno)--;
|
|
|
|
SpinRelease(MMCacheLock);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* mmextend() -- Add a block to the specified relation.
|
|
*
|
|
* This routine returns SM_FAIL or SM_SUCCESS, with errno set as
|
|
* appropriate.
|
|
*/
|
|
int
|
|
mmextend(Relation reln, char *buffer)
|
|
{
|
|
MMRelHashEntry *rentry;
|
|
MMHashEntry *entry;
|
|
int i;
|
|
Oid reldbid;
|
|
int offset;
|
|
bool found;
|
|
MMRelTag rtag;
|
|
MMCacheTag tag;
|
|
|
|
if (reln->rd_rel->relisshared)
|
|
reldbid = (Oid) 0;
|
|
else
|
|
reldbid = MyDatabaseId;
|
|
|
|
tag.mmct_dbid = rtag.mmrt_dbid = reldbid;
|
|
tag.mmct_relid = rtag.mmrt_relid = RelationGetRelid(reln);
|
|
|
|
SpinAcquire(MMCacheLock);
|
|
|
|
if (*MMCurTop == MMNBUFFERS)
|
|
{
|
|
for (i = 0; i < MMNBUFFERS; i++)
|
|
{
|
|
if (MMBlockTags[i].mmct_dbid == 0 &&
|
|
MMBlockTags[i].mmct_relid == 0)
|
|
break;
|
|
}
|
|
if (i == MMNBUFFERS)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
return SM_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
i = *MMCurTop;
|
|
(*MMCurTop)++;
|
|
}
|
|
|
|
rentry = (MMRelHashEntry *) hash_search(MMRelCacheHT, (char *) &rtag,
|
|
HASH_FIND, &found);
|
|
if (rentry == (MMRelHashEntry *) NULL || !found)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
elog(FATAL, "mmextend: rel cache hash table corrupt");
|
|
}
|
|
|
|
tag.mmct_blkno = rentry->mmrhe_nblocks;
|
|
|
|
entry = (MMHashEntry *) hash_search(MMCacheHT, (char *) &tag,
|
|
HASH_ENTER, &found);
|
|
if (entry == (MMHashEntry *) NULL || found)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
elog(FATAL, "mmextend: cache hash table corrupt");
|
|
}
|
|
|
|
entry->mmhe_bufno = i;
|
|
MMBlockTags[i].mmct_dbid = reldbid;
|
|
MMBlockTags[i].mmct_relid = RelationGetRelid(reln);
|
|
MMBlockTags[i].mmct_blkno = rentry->mmrhe_nblocks;
|
|
|
|
/* page numbers are zero-based, so we increment this at the end */
|
|
(rentry->mmrhe_nblocks)++;
|
|
|
|
/* write the extended page */
|
|
offset = (i * BLCKSZ);
|
|
memmove(&(MMBlockCache[offset]), buffer, BLCKSZ);
|
|
|
|
SpinRelease(MMCacheLock);
|
|
|
|
return SM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* mmopen() -- Open the specified relation.
|
|
*/
|
|
int
|
|
mmopen(Relation reln)
|
|
{
|
|
/* automatically successful */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* mmclose() -- Close the specified relation.
|
|
*
|
|
* Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
|
|
*/
|
|
int
|
|
mmclose(Relation reln)
|
|
{
|
|
/* automatically successful */
|
|
return SM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* mmread() -- Read the specified block from a relation.
|
|
*
|
|
* Returns SM_SUCCESS or SM_FAIL.
|
|
*/
|
|
int
|
|
mmread(Relation reln, BlockNumber blocknum, char *buffer)
|
|
{
|
|
MMHashEntry *entry;
|
|
bool found;
|
|
int offset;
|
|
MMCacheTag tag;
|
|
|
|
if (reln->rd_rel->relisshared)
|
|
tag.mmct_dbid = (Oid) 0;
|
|
else
|
|
tag.mmct_dbid = MyDatabaseId;
|
|
|
|
tag.mmct_relid = RelationGetRelid(reln);
|
|
tag.mmct_blkno = blocknum;
|
|
|
|
SpinAcquire(MMCacheLock);
|
|
entry = (MMHashEntry *) hash_search(MMCacheHT, (char *) &tag,
|
|
HASH_FIND, &found);
|
|
|
|
if (entry == (MMHashEntry *) NULL)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
elog(FATAL, "mmread: hash table corrupt");
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
/* reading nonexistent pages is defined to fill them with zeroes */
|
|
SpinRelease(MMCacheLock);
|
|
MemSet(buffer, 0, BLCKSZ);
|
|
return SM_SUCCESS;
|
|
}
|
|
|
|
offset = (entry->mmhe_bufno * BLCKSZ);
|
|
memmove(buffer, &MMBlockCache[offset], BLCKSZ);
|
|
|
|
SpinRelease(MMCacheLock);
|
|
|
|
return SM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* mmwrite() -- Write the supplied block at the appropriate location.
|
|
*
|
|
* Returns SM_SUCCESS or SM_FAIL.
|
|
*/
|
|
int
|
|
mmwrite(Relation reln, BlockNumber blocknum, char *buffer)
|
|
{
|
|
MMHashEntry *entry;
|
|
bool found;
|
|
int offset;
|
|
MMCacheTag tag;
|
|
|
|
if (reln->rd_rel->relisshared)
|
|
tag.mmct_dbid = (Oid) 0;
|
|
else
|
|
tag.mmct_dbid = MyDatabaseId;
|
|
|
|
tag.mmct_relid = RelationGetRelid(reln);
|
|
tag.mmct_blkno = blocknum;
|
|
|
|
SpinAcquire(MMCacheLock);
|
|
entry = (MMHashEntry *) hash_search(MMCacheHT, (char *) &tag,
|
|
HASH_FIND, &found);
|
|
|
|
if (entry == (MMHashEntry *) NULL)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
elog(FATAL, "mmread: hash table corrupt");
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
elog(FATAL, "mmwrite: hash table missing requested page");
|
|
}
|
|
|
|
offset = (entry->mmhe_bufno * BLCKSZ);
|
|
memmove(&MMBlockCache[offset], buffer, BLCKSZ);
|
|
|
|
SpinRelease(MMCacheLock);
|
|
|
|
return SM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* mmflush() -- Synchronously write a block to stable storage.
|
|
*
|
|
* For main-memory relations, this is exactly equivalent to mmwrite().
|
|
*/
|
|
int
|
|
mmflush(Relation reln, BlockNumber blocknum, char *buffer)
|
|
{
|
|
return mmwrite(reln, blocknum, buffer);
|
|
}
|
|
|
|
/*
|
|
* mmblindwrt() -- Write a block to stable storage blind.
|
|
*
|
|
* We have to be able to do this using only the name and OID of
|
|
* the database and relation in which the block belongs.
|
|
*/
|
|
int
|
|
mmblindwrt(char *dbstr,
|
|
char *relstr,
|
|
Oid dbid,
|
|
Oid relid,
|
|
BlockNumber blkno,
|
|
char *buffer)
|
|
{
|
|
return SM_FAIL;
|
|
}
|
|
|
|
/*
|
|
* mmnblocks() -- Get the number of blocks stored in a relation.
|
|
*
|
|
* Returns # of blocks or -1 on error.
|
|
*/
|
|
int
|
|
mmnblocks(Relation reln)
|
|
{
|
|
MMRelTag rtag;
|
|
MMRelHashEntry *rentry;
|
|
bool found;
|
|
int nblocks;
|
|
|
|
if (reln->rd_rel->relisshared)
|
|
rtag.mmrt_dbid = (Oid) 0;
|
|
else
|
|
rtag.mmrt_dbid = MyDatabaseId;
|
|
|
|
rtag.mmrt_relid = RelationGetRelid(reln);
|
|
|
|
SpinAcquire(MMCacheLock);
|
|
|
|
rentry = (MMRelHashEntry *) hash_search(MMRelCacheHT, (char *) &rtag,
|
|
HASH_FIND, &found);
|
|
|
|
if (rentry == (MMRelHashEntry *) NULL)
|
|
{
|
|
SpinRelease(MMCacheLock);
|
|
elog(FATAL, "mmnblocks: rel cache hash table corrupt");
|
|
}
|
|
|
|
if (found)
|
|
nblocks = rentry->mmrhe_nblocks;
|
|
else
|
|
nblocks = -1;
|
|
|
|
SpinRelease(MMCacheLock);
|
|
|
|
return nblocks;
|
|
}
|
|
|
|
/*
|
|
* mmcommit() -- Commit a transaction.
|
|
*
|
|
* Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
|
|
*/
|
|
int
|
|
mmcommit()
|
|
{
|
|
return SM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* mmabort() -- Abort a transaction.
|
|
*/
|
|
|
|
int
|
|
mmabort()
|
|
{
|
|
return SM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* MMShmemSize() -- Declare amount of shared memory we require.
|
|
*
|
|
* The shared memory initialization code creates a block of shared
|
|
* memory exactly big enough to hold all the structures it needs to.
|
|
* This routine declares how much space the main memory storage
|
|
* manager will use.
|
|
*/
|
|
int
|
|
MMShmemSize()
|
|
{
|
|
int size = 0;
|
|
|
|
/*
|
|
* first compute space occupied by the (dbid,relid,blkno) hash table
|
|
*/
|
|
size += hash_estimate_size(MMNBUFFERS,
|
|
0, /* MMHashEntry includes key */
|
|
sizeof(MMHashEntry));
|
|
|
|
/*
|
|
* now do the same for the rel hash table
|
|
*/
|
|
size += hash_estimate_size(MMNRELATIONS,
|
|
0, /* MMRelHashEntry includes key */
|
|
sizeof(MMRelHashEntry));
|
|
|
|
/*
|
|
* finally, add in the memory block we use directly
|
|
*/
|
|
|
|
size += MAXALIGN(BLCKSZ * MMNBUFFERS);
|
|
size += MAXALIGN(sizeof(*MMCurTop));
|
|
size += MAXALIGN(sizeof(*MMCurRelno));
|
|
size += MAXALIGN(MMNBUFFERS * sizeof(MMCacheTag));
|
|
|
|
return size;
|
|
}
|
|
|
|
#endif /* STABLE_MEMORY_STORAGE */
|