diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c index bf0c0ee2678baa26e94d9b556aa712655d00b743..566f00cb25f25fa5c7aace8b28a16d9fa55aa7a8 100644 --- a/src/backend/access/hash/hashpage.c +++ b/src/backend/access/hash/hashpage.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/hash/hashpage.c,v 1.43 2003/11/29 19:51:40 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/access/hash/hashpage.c,v 1.44 2004/08/28 20:31:43 tgl Exp $ * * NOTES * Postgres hash pages look like ordinary relation pages. The opaque @@ -44,14 +44,12 @@ static void _hash_splitbucket(Relation rel, Buffer metabuf, /* * We use high-concurrency locking on hash indexes (see README for an overview - * of the locking rules). There are two cases in which we don't do locking. - * One is when the index is newly created in the current transaction. Since - * the creating transaction has not committed, no one else can see the index, - * and there's no reason to take locks. The second case is for temp - * relations, which no one else can see either. (We still take buffer-level - * locks, but not lmgr locks.) + * of the locking rules). However, we can skip taking lmgr locks when the + * index is local to the current backend (ie, either temp or new in the + * current transaction). No one else can see it, so there's no reason to + * take locks. We still take buffer-level locks, but not lmgr locks. */ -#define USELOCKING(rel) (!((rel)->rd_isnew || (rel)->rd_istemp)) +#define USELOCKING(rel) (!RELATION_IS_LOCAL(rel)) /* diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c index 14eab56aed9c1fa6124b8cd74c1b6bec3e2b3148..30727f49b87baa9594a5d7a4d816f6fdb330db39 100644 --- a/src/backend/access/heap/hio.c +++ b/src/backend/access/heap/hio.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.51 2003/11/29 22:39:39 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.52 2004/08/28 20:31:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -232,7 +232,7 @@ RelationGetBufferForTuple(Relation relation, Size len, * page. We can skip locking for new or temp relations, however, * since no one else could be accessing them. */ - needLock = !(relation->rd_isnew || relation->rd_istemp); + needLock = !RELATION_IS_LOCAL(relation); if (needLock) LockPage(relation, 0, ExclusiveLock); diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c index c8f482545e06f79e96cdeb525375864e50f91f41..6c7edee99f3df7afa760dff0d073b345fef84251 100644 --- a/src/backend/access/nbtree/nbtpage.c +++ b/src/backend/access/nbtree/nbtpage.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtpage.c,v 1.77 2004/07/21 22:31:20 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtpage.c,v 1.78 2004/08/28 20:31:43 tgl Exp $ * * NOTES * Postgres btree pages look like ordinary relation pages. The opaque @@ -483,7 +483,7 @@ _bt_getbuf(Relation rel, BlockNumber blkno, int access) * new page. We can skip locking for new or temp relations, * however, since no one else could be accessing them. */ - needLock = !(rel->rd_isnew || rel->rd_istemp); + needLock = !RELATION_IS_LOCAL(rel); if (needLock) LockPage(rel, 0, ExclusiveLock); diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 25d5048ed82eadc55ad188426ff0ec3d46ce35bc..56ae070acb7de34cb5c01ddbc9eec8f60ee96024 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.179 2004/08/25 18:43:42 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.180 2004/08/28 20:31:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,6 +40,7 @@ #include "utils/inval.h" #include "utils/memutils.h" #include "utils/portal.h" +#include "utils/relcache.h" #include "utils/resowner.h" #include "pgstat.h" @@ -352,7 +353,7 @@ GetCurrentTransactionNestLevel(void) bool TransactionIdIsCurrentTransactionId(TransactionId xid) { - TransactionState s = CurrentTransactionState; + TransactionState s; if (AMI_OVERRIDE) { @@ -363,12 +364,16 @@ TransactionIdIsCurrentTransactionId(TransactionId xid) /* * We will return true for the Xid of the current subtransaction, * any of its subcommitted children, any of its parents, or any of - * their previously subcommitted children. + * their previously subcommitted children. However, a transaction + * being aborted is no longer "current", even though it may still + * have an entry on the state stack. */ - while (s != NULL) + for (s = CurrentTransactionState; s != NULL; s = s->parent) { ListCell *cell; + if (s->state == TRANS_ABORT) + continue; if (TransactionIdEquals(xid, s->transactionIdData)) return true; foreach(cell, s->childXids) @@ -376,8 +381,6 @@ TransactionIdIsCurrentTransactionId(TransactionId xid) if (TransactionIdEquals(xid, lfirst_xid(cell))) return true; } - - s = s->parent; } return false; @@ -2997,6 +3000,8 @@ CommitSubTransaction(void) ResourceOwnerRelease(s->curTransactionOwner, RESOURCE_RELEASE_BEFORE_LOCKS, true, false); + AtEOSubXact_RelationCache(true, s->transactionIdData, + s->parent->transactionIdData); AtEOSubXact_Inval(true); ResourceOwnerRelease(s->curTransactionOwner, RESOURCE_RELEASE_LOCKS, @@ -3090,6 +3095,8 @@ AbortSubTransaction(void) ResourceOwnerRelease(s->curTransactionOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false); + AtEOSubXact_RelationCache(false, s->transactionIdData, + s->parent->transactionIdData); AtEOSubXact_Inval(false); ResourceOwnerRelease(s->curTransactionOwner, RESOURCE_RELEASE_LOCKS, diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index c4787042c08d26c7913c741f4d7a52ba91c73b37..24e4ec0402256a74d5ec95c1d7231759667b49ee 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.207 2004/07/17 03:29:25 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.208 2004/08/28 20:31:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -72,7 +72,7 @@ */ #define RELCACHE_INIT_FILENAME "pg_internal.init" -#define RELCACHE_INIT_FILEMAGIC 0x573261 /* version ID value */ +#define RELCACHE_INIT_FILEMAGIC 0x573262 /* version ID value */ /* * hardcoded tuple descriptors. see include/catalog/pg_attribute.h @@ -835,8 +835,8 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo, * flush the entry.) */ relation->rd_refcnt = 0; - relation->rd_isnailed = 0; - relation->rd_isnew = false; + relation->rd_isnailed = false; + relation->rd_createxact = InvalidTransactionId; relation->rd_istemp = isTempNamespace(relation->rd_rel->relnamespace); /* @@ -886,6 +886,9 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo, RelationCacheInsert(relation); MemoryContextSwitchTo(oldcxt); + /* It's fully valid */ + relation->rd_isvalid = true; + return relation; } @@ -1283,8 +1286,8 @@ formrdesc(const char *relationName, * all entries built with this routine are nailed-in-cache; none are * for new or temp relations. */ - relation->rd_isnailed = 1; - relation->rd_isnew = false; + relation->rd_isnailed = true; + relation->rd_createxact = InvalidTransactionId; relation->rd_istemp = false; /* @@ -1385,6 +1388,9 @@ formrdesc(const char *relationName, * add new reldesc to relcache */ RelationCacheInsert(relation); + + /* It's fully valid */ + relation->rd_isvalid = true; } @@ -1416,7 +1422,7 @@ RelationIdCacheGetRelation(Oid relationId) { RelationIncrementReferenceCount(rd); /* revalidate nailed index if necessary */ - if (rd->rd_isnailed == 2) + if (!rd->rd_isvalid) RelationReloadClassinfo(rd); } @@ -1445,7 +1451,7 @@ RelationSysNameCacheGetRelation(const char *relationName) { RelationIncrementReferenceCount(rd); /* revalidate nailed index if necessary */ - if (rd->rd_isnailed == 2) + if (!rd->rd_isvalid) RelationReloadClassinfo(rd); } @@ -1572,7 +1578,7 @@ RelationClose(Relation relation) #ifdef RELCACHE_FORCE_RELEASE if (RelationHasReferenceCountZero(relation) && - !relation->rd_isnew) + !TransactionIdIsValid(relation->rd_createxact)) RelationClearRelation(relation, false); #endif } @@ -1589,7 +1595,7 @@ RelationClose(Relation relation) * We can't necessarily reread the pg_class row right away; we might be * in a failed transaction when we receive the SI notification. If so, * RelationClearRelation just marks the entry as invalid by setting - * rd_isnailed to 2. This routine is called to fix the entry when it + * rd_isvalid to false. This routine is called to fix the entry when it * is next needed. */ static void @@ -1601,7 +1607,7 @@ RelationReloadClassinfo(Relation relation) Form_pg_class relp; /* Should be called only for invalidated nailed indexes */ - Assert(relation->rd_isnailed == 2 && + Assert(relation->rd_isnailed && !relation->rd_isvalid && relation->rd_rel->relkind == RELKIND_INDEX); /* Read the pg_class row */ buildinfo.infotype = INFO_RELID; @@ -1622,7 +1628,7 @@ RelationReloadClassinfo(Relation relation) heap_freetuple(pg_class_tuple); relation->rd_targblock = InvalidBlockNumber; /* Okay, now it's valid again */ - relation->rd_isnailed = 1; + relation->rd_isvalid = true; } /* @@ -1671,7 +1677,7 @@ RelationClearRelation(Relation relation, bool rebuild) relation->rd_targblock = InvalidBlockNumber; if (relation->rd_rel->relkind == RELKIND_INDEX) { - relation->rd_isnailed = 2; /* needs to be revalidated */ + relation->rd_isvalid = false; /* needs to be revalidated */ if (relation->rd_refcnt > 1) RelationReloadClassinfo(relation); } @@ -1729,15 +1735,15 @@ RelationClearRelation(Relation relation, bool rebuild) { /* * When rebuilding an open relcache entry, must preserve ref count - * and rd_isnew flag. Also attempt to preserve the tupledesc and - * rewrite-rule substructures in place. + * and rd_createxact state. Also attempt to preserve the tupledesc + * and rewrite-rule substructures in place. * * Note that this process does not touch CurrentResourceOwner; * which is good because whatever ref counts the entry may have * do not necessarily belong to that resource owner. */ int old_refcnt = relation->rd_refcnt; - bool old_isnew = relation->rd_isnew; + TransactionId old_createxact = relation->rd_createxact; TupleDesc old_att = relation->rd_att; RuleLock *old_rules = relation->rd_rules; MemoryContext old_rulescxt = relation->rd_rulescxt; @@ -1758,7 +1764,7 @@ RelationClearRelation(Relation relation, bool rebuild) buildinfo.i.info_id); } relation->rd_refcnt = old_refcnt; - relation->rd_isnew = old_isnew; + relation->rd_createxact = old_createxact; if (equalTupleDescs(old_att, relation->rd_att)) { /* needn't flush typcache here */ @@ -1795,7 +1801,7 @@ RelationFlushRelation(Relation relation) { bool rebuild; - if (relation->rd_isnew) + if (TransactionIdIsValid(relation->rd_createxact)) { /* * New relcache entries are always rebuilt, not flushed; else we'd @@ -1941,7 +1947,7 @@ RelationCacheInvalidate(void) } /* Ignore new relations, since they are never SI targets */ - if (relation->rd_isnew) + if (TransactionIdIsValid(relation->rd_createxact)) continue; relcacheInvalsReceived++; @@ -2018,18 +2024,18 @@ AtEOXact_RelationCache(bool isCommit) /* * Is it a relation created in the current transaction? * - * During commit, reset the flag to false, since we are now out of + * During commit, reset the flag to zero, since we are now out of * the creating transaction. During abort, simply delete the * relcache entry --- it isn't interesting any longer. (NOTE: if - * we have forgotten the isnew state of a new relation due to a + * we have forgotten the new-ness of a new relation due to a * forced cache flush, the entry will get deleted anyway by * shared-cache-inval processing of the aborted pg_class * insertion.) */ - if (relation->rd_isnew) + if (TransactionIdIsValid(relation->rd_createxact)) { if (isCommit) - relation->rd_isnew = false; + relation->rd_createxact = InvalidTransactionId; else { RelationClearRelation(relation, false); @@ -2083,6 +2089,56 @@ AtEOXact_RelationCache(bool isCommit) } } +/* + * AtEOSubXact_RelationCache + * + * Clean up the relcache at sub-transaction commit or abort. + * + * Note: this must be called *before* processing invalidation messages. + */ +void +AtEOSubXact_RelationCache(bool isCommit, TransactionId myXid, + TransactionId parentXid) +{ + HASH_SEQ_STATUS status; + RelIdCacheEnt *idhentry; + + hash_seq_init(&status, RelationIdCache); + + while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL) + { + Relation relation = idhentry->reldesc; + + /* + * Is it a relation created in the current subtransaction? + * + * During subcommit, mark it as belonging to the parent, instead. + * During subabort, simply delete the relcache entry. + */ + if (TransactionIdEquals(relation->rd_createxact, myXid)) + { + if (isCommit) + relation->rd_createxact = parentXid; + else + { + Assert(RelationHasReferenceCountZero(relation)); + RelationClearRelation(relation, false); + continue; + } + } + + /* + * Flush any temporary index list. + */ + if (relation->rd_indexvalid == 2) + { + list_free(relation->rd_indexlist); + relation->rd_indexlist = NIL; + relation->rd_indexvalid = 0; + } + } +} + /* * RelationBuildLocalRelation * Build a relcache entry for an about-to-be-created relation, @@ -2126,7 +2182,7 @@ RelationBuildLocalRelation(const char *relname, rel->rd_refcnt = nailit ? 1 : 0; /* it's being created in this transaction */ - rel->rd_isnew = true; + rel->rd_createxact = GetCurrentTransactionId(); /* is it a temporary relation? */ rel->rd_istemp = isTempNamespace(relnamespace); @@ -2137,7 +2193,7 @@ RelationBuildLocalRelation(const char *relname, * want it kicked out. e.g. pg_attribute!!! */ if (nailit) - rel->rd_isnailed = 1; + rel->rd_isnailed = true; /* * create a new tuple descriptor from the one passed in. We do this @@ -2205,6 +2261,9 @@ RelationBuildLocalRelation(const char *relname, */ MemoryContextSwitchTo(oldcxt); + /* It's fully valid */ + rel->rd_isvalid = true; + /* * Caller expects us to pin the returned entry. */ @@ -2326,7 +2385,7 @@ RelationCacheInitializePhase2(void) buildinfo.infotype = INFO_RELNAME; \ buildinfo.i.info_name = (indname); \ ird = RelationBuildDesc(buildinfo, NULL); \ - ird->rd_isnailed = 1; \ + ird->rd_isnailed = true; \ ird->rd_refcnt = 1; \ } while (0) @@ -2677,7 +2736,7 @@ RelationSetIndexList(Relation relation, List *indexIds) { MemoryContext oldcxt; - Assert(relation->rd_isnailed == 1); + Assert(relation->rd_isnailed); /* Copy the list into the cache context (could fail for lack of mem) */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); indexIds = list_copy(indexIds); diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 481cdb2465fd625410641c4996cc64863dd3d7c3..7f0c1351bd6f724f2b73e6bba12f54ac56eb0350 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.76 2004/07/17 03:31:47 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.77 2004/08/28 20:31:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -110,17 +110,18 @@ typedef struct RelationData BlockNumber rd_targblock; /* current insertion target block, or * InvalidBlockNumber */ int rd_refcnt; /* reference count */ - bool rd_isnew; /* rel was created in current xact */ - - /* - * NOTE: rd_isnew should be relied on only for optimization purposes; - * it is possible for new-ness to be "forgotten" (eg, after CLUSTER). - */ bool rd_istemp; /* rel uses the local buffer mgr */ - char rd_isnailed; /* rel is nailed in cache: 0 = no, 1 = yes, - * 2 = yes but possibly invalid */ + bool rd_isnailed; /* rel is nailed in cache */ + bool rd_isvalid; /* relcache entry is valid */ char rd_indexvalid; /* state of rd_indexlist: 0 = not valid, * 1 = valid, 2 = temporarily forced */ + TransactionId rd_createxact; /* rel was created in current xact */ + /* + * rd_createxact is the XID of the highest subtransaction the rel has + * survived into; or zero if the rel was not created in the current + * transaction. This should be relied on only for optimization purposes; + * it is possible for new-ness to be "forgotten" (eg, after CLUSTER). + */ Form_pg_class rd_rel; /* RELATION tuple */ TupleDesc rd_att; /* tuple descriptor */ Oid rd_id; /* relation's object id */ @@ -230,6 +231,16 @@ typedef Relation *RelationPtr; #define RelationGetNamespace(relation) \ ((relation)->rd_rel->relnamespace) +/* + * RELATION_IS_LOCAL + * If a rel is either temp or newly created in the current transaction, + * it can be assumed to be visible only to the current backend. + * + * Beware of multiple eval of argument + */ +#define RELATION_IS_LOCAL(relation) \ + ((relation)->rd_istemp || TransactionIdIsValid((relation)->rd_createxact)) + /* routines in utils/cache/relcache.c */ extern void RelationIncrementReferenceCount(Relation rel); extern void RelationDecrementReferenceCount(Relation rel); diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 227ef591a2c8818fe8c38b9ca9536fb995970e0c..7b244be41399a7db29dbe4b2027cba87d12df485 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.42 2004/07/17 03:31:47 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.43 2004/08/28 20:31:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -66,6 +66,8 @@ extern void RelationCacheInvalidateEntry(Oid relationId, RelFileNode *rnode); extern void RelationCacheInvalidate(void); extern void AtEOXact_RelationCache(bool isCommit); +extern void AtEOSubXact_RelationCache(bool isCommit, TransactionId myXid, + TransactionId parentXid); /* * Routines to help manage rebuilding of relcache init file