*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/portal.h"
+#include "utils/relcache.h"
#include "utils/resowner.h"
#include "pgstat.h"
bool
TransactionIdIsCurrentTransactionId(TransactionId xid)
{
- TransactionState s = CurrentTransactionState;
+ TransactionState s;
if (AMI_OVERRIDE)
{
/*
* 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)
if (TransactionIdEquals(xid, lfirst_xid(cell)))
return true;
}
-
- s = s->parent;
}
return false;
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,
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,
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
#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
* 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);
/*
RelationCacheInsert(relation);
MemoryContextSwitchTo(oldcxt);
+ /* It's fully valid */
+ relation->rd_isvalid = true;
+
return relation;
}
* 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;
/*
* add new reldesc to relcache
*/
RelationCacheInsert(relation);
+
+ /* It's fully valid */
+ relation->rd_isvalid = true;
}
{
RelationIncrementReferenceCount(rd);
/* revalidate nailed index if necessary */
- if (rd->rd_isnailed == 2)
+ if (!rd->rd_isvalid)
RelationReloadClassinfo(rd);
}
{
RelationIncrementReferenceCount(rd);
/* revalidate nailed index if necessary */
- if (rd->rd_isnailed == 2)
+ if (!rd->rd_isvalid)
RelationReloadClassinfo(rd);
}
#ifdef RELCACHE_FORCE_RELEASE
if (RelationHasReferenceCountZero(relation) &&
- !relation->rd_isnew)
+ !TransactionIdIsValid(relation->rd_createxact))
RelationClearRelation(relation, false);
#endif
}
* 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
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;
heap_freetuple(pg_class_tuple);
relation->rd_targblock = InvalidBlockNumber;
/* Okay, now it's valid again */
- relation->rd_isnailed = 1;
+ relation->rd_isvalid = true;
}
/*
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);
}
{
/*
* 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;
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 */
{
bool rebuild;
- if (relation->rd_isnew)
+ if (TransactionIdIsValid(relation->rd_createxact))
{
/*
* New relcache entries are always rebuilt, not flushed; else we'd
}
/* Ignore new relations, since they are never SI targets */
- if (relation->rd_isnew)
+ if (TransactionIdIsValid(relation->rd_createxact))
continue;
relcacheInvalsReceived++;
/*
* 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);
}
}
+/*
+ * 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,
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);
* 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
*/
MemoryContextSwitchTo(oldcxt);
+ /* It's fully valid */
+ rel->rd_isvalid = true;
+
/*
* Caller expects us to pin the returned entry.
*/
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)
{
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
#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);