/*------------------------------------------------------------------------- * * 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.1.1.1 1996/07/09 06:21:13 scrappy Exp $ * *------------------------------------------------------------------------- */ #include #include "postgres.h" #include "machine.h" /* in port/ directory (needed for BLCKSZ) */ #include "storage/buf.h" #include "storage/bufmgr.h" #include "storage/ipc.h" /* for OIDGENLOCKID */ #include "utils/rel.h" #include "utils/elog.h" #include "access/heapam.h" #include "access/transam.h" /* where the declarations go */ #include "access/xact.h" /* where the declarations go */ #include "catalog/catname.h" /* ---------- * note: we reserve the first 16384 object ids for internal use. * oid's less than this appear in the .bki files. the choice of * 16384 is completely arbitrary. * ---------- */ #define BootstrapObjectIdData 16384 /* --------------------- * 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; /* ---------------- * 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)); WriteBuffer(buf); } /* -------------------------------- * 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, "VariableRelationPutNextXid: 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--; }