postgresql/src/backend/access/transam/varsup.c

653 lines
18 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* varsup.c--
* postgres variable relation support routines
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/transam/varsup.c,v 1.8 1997/08/12 22:51:58 momjian Exp $
*
*-------------------------------------------------------------------------
*/
1996-10-21 09:15:18 +02:00
1996-11-05 12:12:33 +01:00
#include <postgres.h>
1996-11-05 12:12:33 +01:00
#include <access/transam.h>
#include <storage/spin.h>
#include <access/xact.h>
#include <access/heapam.h>
#include <catalog/catname.h>
/* ---------------------
* spin lock for oid generation
* ---------------------
*/
int OidGenLockId;
/* ----------------------------------------------------------------
* variable relation query/update routines
* ----------------------------------------------------------------
*/
/* --------------------------------
* VariableRelationGetNextXid
* --------------------------------
*/
void
VariableRelationGetNextXid(TransactionId *xidP)
{
Buffer buf;
VariableRelationContents var;
/* ----------------
* We assume that a spinlock has been acquire to guarantee
* exclusive access to the variable relation.
* ----------------
*/
/* ----------------
* do nothing before things are initialized
* ----------------
*/
if (! RelationIsValid(VariableRelation))
return;
/* ----------------
* read the variable page, get the the nextXid field and
* release the buffer
* ----------------
*/
buf = ReadBuffer(VariableRelation, 0);
if (! BufferIsValid(buf))
{
SpinRelease(OidGenLockId);
elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed");
}
var = (VariableRelationContents) BufferGetBlock(buf);
TransactionIdStore(var->nextXidData, xidP);
ReleaseBuffer(buf);
}
/* --------------------------------
* VariableRelationGetLastXid
* --------------------------------
*/
void
VariableRelationGetLastXid(TransactionId *xidP)
{
Buffer buf;
VariableRelationContents var;
/* ----------------
* We assume that a spinlock has been acquire to guarantee
* exclusive access to the variable relation.
* ----------------
*/
/* ----------------
* do nothing before things are initialized
* ----------------
*/
if (! RelationIsValid(VariableRelation))
return;
/* ----------------
* read the variable page, get the the lastXid field and
* release the buffer
* ----------------
*/
buf = ReadBuffer(VariableRelation, 0);
if (! BufferIsValid(buf))
{
SpinRelease(OidGenLockId);
elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed");
}
var = (VariableRelationContents) BufferGetBlock(buf);
TransactionIdStore(var->lastXidData, xidP);
ReleaseBuffer(buf);
}
/* --------------------------------
* VariableRelationPutNextXid
* --------------------------------
*/
void
VariableRelationPutNextXid(TransactionId xid)
{
Buffer buf;
VariableRelationContents var;
int flushmode;
/* ----------------
* We assume that a spinlock has been acquire to guarantee
* exclusive access to the variable relation.
* ----------------
*/
/* ----------------
* do nothing before things are initialized
* ----------------
*/
if (! RelationIsValid(VariableRelation))
return;
/* ----------------
* read the variable page, update the nextXid field and
* write the page back out to disk.
* ----------------
*/
buf = ReadBuffer(VariableRelation, 0);
if (! BufferIsValid(buf))
{
SpinRelease(OidGenLockId);
elog(WARN, "VariableRelationPutNextXid: ReadBuffer failed");
}
var = (VariableRelationContents) BufferGetBlock(buf);
TransactionIdStore(xid, &(var->nextXidData));
flushmode = SetBufferWriteMode (BUFFER_FLUSH_WRITE);
WriteBuffer(buf);
SetBufferWriteMode (flushmode);
}
/* --------------------------------
* VariableRelationPutLastXid
* --------------------------------
*/
void
VariableRelationPutLastXid(TransactionId xid)
{
Buffer buf;
VariableRelationContents var;
/* ----------------
* We assume that a spinlock has been acquire to guarantee
* exclusive access to the variable relation.
* ----------------
*/
/* ----------------
* do nothing before things are initialized
* ----------------
*/
if (! RelationIsValid(VariableRelation))
return;
/* ----------------
* read the variable page, update the lastXid field and
* force the page back out to disk.
* ----------------
*/
buf = ReadBuffer(VariableRelation, 0);
if (! BufferIsValid(buf))
{
SpinRelease(OidGenLockId);
elog(WARN, "VariableRelationPutLastXid: ReadBuffer failed");
}
var = (VariableRelationContents) BufferGetBlock(buf);
TransactionIdStore(xid, &(var->lastXidData));
WriteBuffer(buf);
}
/* --------------------------------
* VariableRelationGetNextOid
* --------------------------------
*/
void
VariableRelationGetNextOid(Oid *oid_return)
{
Buffer buf;
VariableRelationContents var;
/* ----------------
* We assume that a spinlock has been acquire to guarantee
* exclusive access to the variable relation.
* ----------------
*/
/* ----------------
* if the variable relation is not initialized, then we
* assume we are running at bootstrap time and so we return
* an invalid object id -- during this time GetNextBootstrapObjectId
* should be called instead..
* ----------------
*/
if (! RelationIsValid(VariableRelation)) {
if (PointerIsValid(oid_return))
(*oid_return) = InvalidOid;
return;
}
/* ----------------
* read the variable page, get the the nextOid field and
* release the buffer
* ----------------
*/
buf = ReadBuffer(VariableRelation, 0);
if (! BufferIsValid(buf))
{
SpinRelease(OidGenLockId);
elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed");
}
var = (VariableRelationContents) BufferGetBlock(buf);
if (PointerIsValid(oid_return)) {
/* ----------------
* nothing up my sleeve... what's going on here is that this code
* is guaranteed never to be called until all files in data/base/
* are created, and the template database exists. at that point,
* we want to append a pg_database tuple. the first time we do
* this, the oid stored in pg_variable will be bogus, so we use
* a bootstrap value defined at the top of this file.
*
* this comment no longer holds true. This code is called before
* all of the files in data/base are created and you can't rely
* on system oid's to be less than BootstrapObjectIdData. mer 9/18/91
* ----------------
*/
if (OidIsValid(var->nextOid))
(*oid_return) = var->nextOid;
else
(*oid_return) = BootstrapObjectIdData;
}
ReleaseBuffer(buf);
}
/* --------------------------------
* VariableRelationPutNextOid
* --------------------------------
*/
void
VariableRelationPutNextOid(Oid *oidP)
{
Buffer buf;
VariableRelationContents var;
/* ----------------
* We assume that a spinlock has been acquire to guarantee
* exclusive access to the variable relation.
* ----------------
*/
/* ----------------
* do nothing before things are initialized
* ----------------
*/
if (! RelationIsValid(VariableRelation))
return;
/* ----------------
* sanity check
* ----------------
*/
if (! PointerIsValid(oidP))
{
SpinRelease(OidGenLockId);
elog(WARN, "VariableRelationPutNextOid: invalid oid pointer");
}
/* ----------------
* read the variable page, update the nextXid field and
* write the page back out to disk.
* ----------------
*/
buf = ReadBuffer(VariableRelation, 0);
if (! BufferIsValid(buf))
{
SpinRelease(OidGenLockId);
elog(WARN, "VariableRelationPutNextOid: ReadBuffer failed");
}
var = (VariableRelationContents) BufferGetBlock(buf);
var->nextOid = (*oidP);
WriteBuffer(buf);
}
/* ----------------------------------------------------------------
* transaction id generation support
* ----------------------------------------------------------------
*/
/* ----------------
* GetNewTransactionId
*
* In the version 2 transaction system, transaction id's are
* restricted in several ways.
*
* First, all transaction id's are even numbers (4, 88, 121342, etc).
* This means the binary representation of the number will never
* have the least significent bit set. This bit is reserved to
* indicate that the transaction id does not in fact hold an XID,
* but rather a commit time. This makes it possible for the
* vaccuum daemon to disgard information from the log and time
* relations for committed tuples. This is important when archiving
* tuples to an optical disk because tuples with commit times
* stored in their xid fields will not need to consult the log
* and time relations.
*
* Second, since we may someday preform compression of the data
* in the log and time relations, we cause the numbering of the
* transaction ids to begin at 512. This means that some space
* on the page of the log and time relations corresponding to
* transaction id's 0 - 510 will never be used. This space is
* in fact used to store the version number of the postgres
* transaction log and will someday store compression information
* about the log.
*
* Lastly, rather then access the variable relation each time
* a backend requests a new transction id, we "prefetch" 32
* transaction id's by incrementing the nextXid stored in the
* var relation by 64 (remember only even xid's are legal) and then
* returning these id's one at a time until they are exhausted.
* This means we reduce the number of accesses to the variable
* relation by 32 for each backend.
*
* Note: 32 has no special significance. We don't want the
* number to be too large because if when the backend
* terminates, we lose the xid's we cached.
*
* ----------------
*/
#define VAR_XID_PREFETCH 32
static int prefetched_xid_count = 0;
static TransactionId next_prefetched_xid;
void
GetNewTransactionId(TransactionId *xid)
{
TransactionId nextid;
/* ----------------
* during bootstrap initialization, we return the special
* bootstrap transaction id.
* ----------------
*/
if (AMI_OVERRIDE) {
TransactionIdStore(AmiTransactionId, xid);
return;
}
/* ----------------
* if we run out of prefetched xids, then we get some
* more before handing them out to the caller.
* ----------------
*/
if (prefetched_xid_count == 0) {
/* ----------------
* obtain exclusive access to the variable relation page
*
* get the "next" xid from the variable relation
* and save it in the prefetched id.
* ----------------
*/
SpinAcquire(OidGenLockId);
VariableRelationGetNextXid(&nextid);
TransactionIdStore(nextid, &next_prefetched_xid);
/* ----------------
* now increment the variable relation's next xid
* and reset the prefetched_xid_count. We multiply
* the id by two because our xid's are always even.
* ----------------
*/
prefetched_xid_count = VAR_XID_PREFETCH;
TransactionIdAdd(&nextid, prefetched_xid_count);
VariableRelationPutNextXid(nextid);
SpinRelease(OidGenLockId);
}
/* ----------------
* return the next prefetched xid in the pointer passed by
* the user and decrement the prefetch count. We add two
* to id we return the next time this is called because our
* transaction ids are always even.
*
* XXX Transaction Ids used to be even as the low order bit was
* used to determine commit status. This is no long true so
* we now use even and odd transaction ids. -mer 5/26/92
* ----------------
*/
TransactionIdStore(next_prefetched_xid, xid);
TransactionIdAdd(&next_prefetched_xid, 1);
prefetched_xid_count--;
}
/* ----------------
* UpdateLastCommittedXid
* ----------------
*/
void
UpdateLastCommittedXid(TransactionId xid)
{
TransactionId lastid;
/* we assume that spinlock OidGenLockId has been acquired
* prior to entering this function
*/
/* ----------------
* get the "last committed" transaction id from
* the variable relation page.
* ----------------
*/
VariableRelationGetLastXid(&lastid);
/* ----------------
* if the transaction id is greater than the last committed
* transaction then we update the last committed transaction
* in the variable relation.
* ----------------
*/
if (TransactionIdIsLessThan(lastid, xid))
VariableRelationPutLastXid(xid);
}
/* ----------------------------------------------------------------
* object id generation support
* ----------------------------------------------------------------
*/
/* ----------------
* GetNewObjectIdBlock
*
* This support function is used to allocate a block of object ids
* of the given size. applications wishing to do their own object
* id assignments should use this
* ----------------
*/
void
GetNewObjectIdBlock(Oid *oid_return, /* place to return the new object id */
int oid_block_size) /* number of oids desired */
{
Oid nextoid;
/* ----------------
* SOMEDAY obtain exclusive access to the variable relation page
* That someday is today -mer 6 Aug 1992
* ----------------
*/
SpinAcquire(OidGenLockId);
/* ----------------
* get the "next" oid from the variable relation
* and give it to the caller.
* ----------------
*/
VariableRelationGetNextOid(&nextoid);
if (PointerIsValid(oid_return))
(*oid_return) = nextoid;
/* ----------------
* now increment the variable relation's next oid
* field by the size of the oid block requested.
* ----------------
*/
nextoid += oid_block_size;
VariableRelationPutNextOid(&nextoid);
/* ----------------
* SOMEDAY relinquish our lock on the variable relation page
* That someday is today -mer 6 Apr 1992
* ----------------
*/
SpinRelease(OidGenLockId);
}
/* ----------------
* GetNewObjectId
*
* This function allocates and parses out object ids. Like
* GetNewTransactionId(), it "prefetches" 32 object ids by
* incrementing the nextOid stored in the var relation by 32 and then
* returning these id's one at a time until they are exhausted.
* This means we reduce the number of accesses to the variable
* relation by 32 for each backend.
*
* Note: 32 has no special significance. We don't want the
* number to be too large because if when the backend
* terminates, we lose the oids we cached.
*
* ----------------
*/
#define VAR_OID_PREFETCH 32
static int prefetched_oid_count = 0;
static Oid next_prefetched_oid;
void
GetNewObjectId(Oid *oid_return) /* place to return the new object id */
{
/* ----------------
* if we run out of prefetched oids, then we get some
* more before handing them out to the caller.
* ----------------
*/
if (prefetched_oid_count == 0) {
int oid_block_size = VAR_OID_PREFETCH;
/* ----------------
* during bootstrap time, we want to allocate oids
* one at a time. Otherwise there might be some
* bootstrap oid's left in the block we prefetch which
* would be passed out after the variable relation was
* initialized. This would be bad.
* ----------------
*/
if (! RelationIsValid(VariableRelation))
VariableRelation = heap_openr(VariableRelationName);
/* ----------------
* get a new block of prefetched object ids.
* ----------------
*/
GetNewObjectIdBlock(&next_prefetched_oid, oid_block_size);
/* ----------------
* now reset the prefetched_oid_count.
* ----------------
*/
prefetched_oid_count = oid_block_size;
}
/* ----------------
* return the next prefetched oid in the pointer passed by
* the user and decrement the prefetch count.
* ----------------
*/
if (PointerIsValid(oid_return))
(*oid_return) = next_prefetched_oid;
next_prefetched_oid++;
prefetched_oid_count--;
}
void
CheckMaxObjectId(Oid assigned_oid)
{
Oid pass_oid;
if (prefetched_oid_count == 0) /* make sure next/max is set, or reload */
GetNewObjectId(&pass_oid);
/* ----------------
* If we are below prefetched limits, do nothing
* ----------------
*/
if (assigned_oid < next_prefetched_oid)
return;
/* ----------------
* If we are here, we are coming from a 'copy from' with oid's
*
* If we are in the prefetched oid range, just bump it up
*
* ----------------
*/
if (assigned_oid <= next_prefetched_oid + prefetched_oid_count - 1)
{
prefetched_oid_count -= assigned_oid - next_prefetched_oid + 1;
next_prefetched_oid = assigned_oid + 1;
return;
}
/* ----------------
* We have exceeded the prefetch oid range
*
* We should lock the database and kill all other backends
* but we are loading oid's that we can not guarantee are unique
* anyway, so we must rely on the user
*
*
* We now:
* set the variable relation with the new max oid
* force the backend to reload its oid cache
*
* We use the oid cache so we don't have to update the variable
* relation every time
*
* ----------------
*/
pass_oid = assigned_oid;
VariableRelationPutNextOid(&pass_oid); /* not modified */
prefetched_oid_count = 0; /* force reload */
pass_oid = assigned_oid;
GetNewObjectId(&pass_oid); /* throw away returned oid */
}