Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b4a607c

Browse files
committed
Modify RelationFlushRelation so that if the relcache entry
has positive refcount, it is rebuilt from pg_class data. This ensures that relcache entries will track changes made by other backends. Formerly, a shared inval report would just be ignored if it happened to arrive while the relcache entry was in use. Also, fix relcache to reset ref counts to zero during transaction abort. Finally, change LockRelation() so that it checks for shared inval reports after obtaining the lock. In this way, once any kind of lock has been obtained on a rel, we can trust the relcache entry to be up-to-date.
1 parent 8add6d7 commit b4a607c

File tree

4 files changed

+135
-53
lines changed

4 files changed

+135
-53
lines changed

src/backend/access/transam/xact.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.47 1999/08/08 20:12:52 tgl Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.48 1999/09/04 18:42:15 tgl Exp $
1111
*
1212
* NOTES
1313
* Transaction aborts can now occur two ways:
@@ -755,6 +755,7 @@ static void
755755
AtAbort_Cache()
756756
{
757757
RegisterInvalid(false);
758+
RelationCacheAbort();
758759
}
759760

760761
/* --------------------------------

src/backend/storage/lmgr/lmgr.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lmgr.c,v 1.33 1999/07/17 20:17:46 momjian Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lmgr.c,v 1.34 1999/09/04 18:42:14 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -22,6 +22,7 @@
2222
#include "postgres.h"
2323
#include "access/transam.h"
2424
#include "catalog/catalog.h"
25+
#include "utils/inval.h"
2526

2627
extern Oid MyDatabaseId;
2728

@@ -161,7 +162,16 @@ LockRelation(Relation relation, LOCKMODE lockmode)
161162
tag.objId.blkno = InvalidBlockNumber;
162163

163164
LockAcquire(LockTableId, &tag, lockmode);
164-
return;
165+
166+
/*
167+
* Check to see if the relcache entry has been invalidated
168+
* while we were waiting to lock it. If so, rebuild it,
169+
* or elog() trying. Increment the refcount to ensure that
170+
* RelationFlushRelation will rebuild it and not just delete it.
171+
*/
172+
RelationIncrementReferenceCount(relation);
173+
DiscardInvalid();
174+
RelationDecrementReferenceCount(relation);
165175
}
166176

167177
/*

src/backend/utils/cache/relcache.c

Lines changed: 118 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.68 1999/09/02 02:57:50 tgl Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.69 1999/09/04 18:42:13 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -61,6 +61,8 @@
6161
static void RelationFlushRelation(Relation *relationPtr,
6262
bool onlyFlushReferenceCountZero);
6363
static Relation RelationNameCacheGetRelation(char *relationName);
64+
static void RelationCacheAbortWalker(Relation *relationPtr,
65+
int dummy);
6466
static void init_irels(void);
6567
static void write_irels(void);
6668

@@ -213,14 +215,16 @@ static void RelationFlushIndexes(Relation *r, Oid accessMethodId);
213215
static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo);
214216
static HeapTuple scan_pg_rel_seq(RelationBuildDescInfo buildinfo);
215217
static HeapTuple scan_pg_rel_ind(RelationBuildDescInfo buildinfo);
216-
static Relation AllocateRelationDesc(u_int natts, Form_pg_class relp);
218+
static Relation AllocateRelationDesc(Relation relation, u_int natts,
219+
Form_pg_class relp);
217220
static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
218221
Relation relation, u_int natts);
219222
static void build_tupdesc_seq(RelationBuildDescInfo buildinfo,
220223
Relation relation, u_int natts);
221224
static void build_tupdesc_ind(RelationBuildDescInfo buildinfo,
222225
Relation relation, u_int natts);
223-
static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo);
226+
static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo,
227+
Relation oldrelation);
224228
static void IndexedAccessMethodInitialize(Relation relation);
225229
static void AttrDefaultFetch(Relation relation);
226230
static void RelCheckFetch(Relation relation);
@@ -405,12 +409,16 @@ scan_pg_rel_ind(RelationBuildDescInfo buildinfo)
405409
*
406410
* This is used to allocate memory for a new relation descriptor
407411
* and initialize the rd_rel field.
412+
*
413+
* If 'relation' is NULL, allocate a new RelationData object.
414+
* If not, reuse the given object (that path is taken only when
415+
* we have to rebuild a relcache entry during RelationFlushRelation).
408416
* ----------------
409417
*/
410418
static Relation
411-
AllocateRelationDesc(u_int natts, Form_pg_class relp)
419+
AllocateRelationDesc(Relation relation, u_int natts,
420+
Form_pg_class relp)
412421
{
413-
Relation relation;
414422
Size len;
415423
Form_pg_class relationForm;
416424

@@ -424,18 +432,22 @@ AllocateRelationDesc(u_int natts, Form_pg_class relp)
424432
memmove((char *) relationForm, (char *) relp, CLASS_TUPLE_SIZE);
425433

426434
/* ----------------
427-
* allocate space for new relation descriptor
435+
* allocate space for new relation descriptor, if needed
428436
*/
429-
len = sizeof(RelationData) + 10; /* + 10 is voodoo XXX mao */
437+
len = sizeof(RelationData);
430438

431-
relation = (Relation) palloc(len);
439+
if (relation == NULL)
440+
relation = (Relation) palloc(len);
432441

433442
/* ----------------
434443
* clear new reldesc
435444
* ----------------
436445
*/
437446
MemSet((char *) relation, 0, len);
438447

448+
/* make sure relation is marked as having no open file yet */
449+
relation->rd_fd = -1;
450+
439451
/* initialize attribute tuple form */
440452
relation->rd_att = CreateTemplateTupleDesc(natts);
441453

@@ -737,6 +749,11 @@ RelationBuildRuleLock(Relation relation)
737749
/* --------------------------------
738750
* RelationBuildDesc
739751
*
752+
* Build a relation descriptor --- either a new one, or by
753+
* recycling the given old relation object. The latter case
754+
* supports rebuilding a relcache entry without invalidating
755+
* pointers to it.
756+
*
740757
* To build a relation descriptor, we have to allocate space,
741758
* open the underlying unix file and initialize the following
742759
* fields:
@@ -758,7 +775,8 @@ RelationBuildRuleLock(Relation relation)
758775
* --------------------------------
759776
*/
760777
static Relation
761-
RelationBuildDesc(RelationBuildDescInfo buildinfo)
778+
RelationBuildDesc(RelationBuildDescInfo buildinfo,
779+
Relation oldrelation)
762780
{
763781
File fd;
764782
Relation relation;
@@ -803,7 +821,7 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo)
803821
* initialize relation->rd_rel and get the access method id.
804822
* ----------------
805823
*/
806-
relation = AllocateRelationDesc(natts, relp);
824+
relation = AllocateRelationDesc(oldrelation, natts, relp);
807825
relam = relation->rd_rel->relam;
808826

809827
/* ----------------
@@ -833,9 +851,6 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo)
833851

834852
/* ----------------
835853
* initialize the tuple descriptor (relation->rd_att).
836-
* remember, rd_att is an array of attribute pointers that lives
837-
* off the end of the relation descriptor structure so space was
838-
* already allocated for it by AllocateRelationDesc.
839854
* ----------------
840855
*/
841856
RelationBuildTupleDesc(buildinfo, relation, natts);
@@ -1153,7 +1168,7 @@ RelationIdGetRelation(Oid relationId)
11531168
buildinfo.infotype = INFO_RELID;
11541169
buildinfo.i.info_id = relationId;
11551170

1156-
rd = RelationBuildDesc(buildinfo);
1171+
rd = RelationBuildDesc(buildinfo, NULL);
11571172
return rd;
11581173
}
11591174

@@ -1193,7 +1208,7 @@ RelationNameGetRelation(char *relationName)
11931208
buildinfo.infotype = INFO_RELNAME;
11941209
buildinfo.i.info_name = relationName;
11951210

1196-
rd = RelationBuildDesc(buildinfo);
1211+
rd = RelationBuildDesc(buildinfo, NULL);
11971212
return rd;
11981213
}
11991214

@@ -1236,58 +1251,82 @@ RelationClose(Relation relation)
12361251
/* --------------------------------
12371252
* RelationFlushRelation
12381253
*
1239-
* Actually blows away a relation... RelationFree doesn't do
1254+
* Actually blows away a relation cache entry... RelationFree doesn't do
12401255
* anything anymore.
12411256
* --------------------------------
12421257
*/
12431258
static void
12441259
RelationFlushRelation(Relation *relationPtr,
12451260
bool onlyFlushReferenceCountZero)
12461261
{
1247-
MemoryContext oldcxt;
12481262
Relation relation = *relationPtr;
1263+
MemoryContext oldcxt;
1264+
1265+
/*
1266+
* Make sure smgr and lower levels close the relation's files,
1267+
* if they weren't closed already. We do this unconditionally;
1268+
* if the relation is not deleted, the next smgr access should
1269+
* reopen the files automatically. This ensures that the low-level
1270+
* file access state is updated after, say, a vacuum truncation.
1271+
* NOTE: this call is a no-op if the relation's smgr file is already
1272+
* closed or unlinked.
1273+
*/
1274+
smgrclose(DEFAULT_SMGR, relation);
12491275

12501276
if (relation->rd_isnailed)
12511277
{
1252-
/* this is a nailed special relation for bootstraping */
1278+
/* this is a nailed special relation for bootstrapping */
12531279
return;
12541280
}
12551281

1256-
if (!onlyFlushReferenceCountZero ||
1257-
RelationHasReferenceCountZero(relation))
1258-
{
1259-
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
1282+
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
12601283

1261-
/* make sure smgr and lower levels close the relation's files,
1262-
* if they weren't closed already
1263-
*/
1264-
smgrclose(DEFAULT_SMGR, relation);
1284+
/* Remove relation from hash tables
1285+
*
1286+
* Note: we might be reinserting it momentarily, but we must not have it
1287+
* visible in the hash tables until it's valid again, so don't try to
1288+
* optimize this away...
1289+
*/
1290+
RelationCacheDelete(relation);
1291+
1292+
/* Clear out catcache's entries for this relation */
1293+
SystemCacheRelationFlushed(RelationGetRelid(relation));
12651294

1266-
RelationCacheDelete(relation);
1295+
/* Free all the subsidiary data structures of the relcache entry */
1296+
FreeTupleDesc(relation->rd_att);
1297+
FreeTriggerDesc(relation);
1298+
pfree(RelationGetLockInfo(relation));
1299+
pfree(RelationGetForm(relation));
12671300

1268-
FreeTupleDesc(relation->rd_att);
1269-
SystemCacheRelationFlushed(RelationGetRelid(relation));
1301+
/* If we're really done with the relcache entry, blow it away.
1302+
* But if someone is still using it, reconstruct the whole deal
1303+
* without moving the physical RelationData record (so that the
1304+
* someone's pointer is still valid). Preserve ref count, too.
1305+
*/
1306+
if (!onlyFlushReferenceCountZero ||
1307+
RelationHasReferenceCountZero(relation))
1308+
{
1309+
pfree(relation);
1310+
}
1311+
else
1312+
{
1313+
uint16 old_refcnt = relation->rd_refcnt;
1314+
RelationBuildDescInfo buildinfo;
12701315

1271-
FreeTriggerDesc(relation);
1316+
buildinfo.infotype = INFO_RELID;
1317+
buildinfo.i.info_id = RelationGetRelid(relation);
12721318

1273-
#ifdef NOT_USED
1274-
if (relation->rd_rules)
1319+
if (RelationBuildDesc(buildinfo, relation) != relation)
12751320
{
1276-
int j;
1277-
1278-
for (j = 0; j < relation->rd_rules->numLocks; j++)
1279-
pfree(relation->rd_rules->rules[j]);
1280-
pfree(relation->rd_rules->rules);
1281-
pfree(relation->rd_rules);
1321+
/* Should only get here if relation was deleted */
1322+
pfree(relation);
1323+
elog(ERROR, "RelationFlushRelation: relation %u deleted while still in use",
1324+
buildinfo.i.info_id);
12821325
}
1283-
#endif
1284-
1285-
pfree(RelationGetLockInfo(relation));
1286-
pfree(RelationGetForm(relation));
1287-
pfree(relation);
1288-
1289-
MemoryContextSwitchTo(oldcxt);
1326+
RelationSetReferenceCount(relation, old_refcnt);
12901327
}
1328+
1329+
MemoryContextSwitchTo(oldcxt);
12911330
}
12921331

12931332
/* --------------------------------
@@ -1442,6 +1481,36 @@ RelationCacheInvalidate(bool onlyFlushReferenceCountZero)
14421481
}
14431482
}
14441483

1484+
/*
1485+
* RelationCacheAbort
1486+
*
1487+
* Clean up the relcache at transaction abort.
1488+
*
1489+
* What we need to do here is reset relcache entry ref counts to
1490+
* their normal not-in-a-transaction state. A ref count may be
1491+
* too high because some routine was exited by elog() between
1492+
* incrementing and decrementing the count.
1493+
*
1494+
* XXX Maybe we should do this at transaction commit, too, in case
1495+
* someone forgets to decrement a refcount in a non-error path?
1496+
*/
1497+
void
1498+
RelationCacheAbort(void)
1499+
{
1500+
HashTableWalk(RelationNameCache, (HashtFunc) RelationCacheAbortWalker,
1501+
0);
1502+
}
1503+
1504+
static void
1505+
RelationCacheAbortWalker(Relation *relationPtr, int dummy)
1506+
{
1507+
Relation relation = *relationPtr;
1508+
1509+
if (relation->rd_isnailed)
1510+
RelationSetReferenceCount(relation, 1);
1511+
else
1512+
RelationSetReferenceCount(relation, 0);
1513+
}
14451514

14461515
/* --------------------------------
14471516
* RelationRegisterRelation -
@@ -1523,7 +1592,7 @@ RelationPurgeLocalRelation(bool xactCommitted)
15231592
reln->rd_myxactonly = FALSE;
15241593

15251594
if (!IsBootstrapProcessingMode())
1526-
RelationFlushRelation(&reln, FALSE);
1595+
RelationFlushRelation(&reln, false);
15271596

15281597
newlyCreatedRelns = lnext(newlyCreatedRelns);
15291598
pfree(l);
@@ -2010,15 +2079,15 @@ write_irels(void)
20102079

20112080
bi.infotype = INFO_RELNAME;
20122081
bi.i.info_name = AttributeNumIndex;
2013-
irel[0] = RelationBuildDesc(bi);
2082+
irel[0] = RelationBuildDesc(bi, NULL);
20142083
irel[0]->rd_isnailed = true;
20152084

20162085
bi.i.info_name = ClassNameIndex;
2017-
irel[1] = RelationBuildDesc(bi);
2086+
irel[1] = RelationBuildDesc(bi, NULL);
20182087
irel[1]->rd_isnailed = true;
20192088

20202089
bi.i.info_name = ClassOidIndex;
2021-
irel[2] = RelationBuildDesc(bi);
2090+
irel[2] = RelationBuildDesc(bi, NULL);
20222091
irel[2]->rd_isnailed = true;
20232092

20242093
SetProcessingMode(oldmode);

src/include/utils/relcache.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*
77
* Copyright (c) 1994, Regents of the University of California
88
*
9-
* $Id: relcache.h,v 1.13 1999/07/15 23:04:23 momjian Exp $
9+
* $Id: relcache.h,v 1.14 1999/09/04 18:42:11 tgl Exp $
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
@@ -34,6 +34,8 @@ extern void RelationRegisterRelation(Relation relation);
3434
extern void RelationPurgeLocalRelation(bool xactComitted);
3535
extern void RelationInitialize(void);
3636

37+
extern void RelationCacheAbort(void);
38+
3739
/*
3840
* both vacuum.c and relcache.c need to know the name of the relcache init file
3941
*/

0 commit comments

Comments
 (0)