diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2006-07-31 20:09:10 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2006-07-31 20:09:10 +0000 |
commit | 09d3670df3e4593be1d2299a62d982829016b847 (patch) | |
tree | 7a62e91c1cf595d0334dd2c196d1c79835cc267b /src/backend/access/index/indexam.c | |
parent | 4cd72b53b9975bab5f4ca0792cf4f54c84829404 (diff) | |
download | postgresql-09d3670df3e4593be1d2299a62d982829016b847.tar.gz postgresql-09d3670df3e4593be1d2299a62d982829016b847.zip |
Change the relation_open protocol so that we obtain lock on a relation
(table or index) before trying to open its relcache entry. This fixes
race conditions in which someone else commits a change to the relation's
catalog entries while we are in process of doing relcache load. Problems
of that ilk have been reported sporadically for years, but it was not
really practical to fix until recently --- for instance, the recent
addition of WAL-log support for in-place updates helped.
Along the way, remove pg_am.amconcurrent: all AMs are now expected to support
concurrent update.
Diffstat (limited to 'src/backend/access/index/indexam.c')
-rw-r--r-- | src/backend/access/index/indexam.c | 110 |
1 files changed, 30 insertions, 80 deletions
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 900b34263d8..2663876f494 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -8,11 +8,10 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.93 2006/05/07 01:21:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.94 2006/07/31 20:08:59 tgl Exp $ * * INTERFACE ROUTINES * index_open - open an index relation by relation OID - * index_openrv - open an index relation specified by a RangeVar * index_close - close an index relation * index_beginscan - start a scan of an index with amgettuple * index_beginscan_multi - start a scan of an index with amgetmulti @@ -111,7 +110,6 @@ do { \ } while(0) static IndexScanDesc index_beginscan_internal(Relation indexRelation, - bool need_index_lock, int nkeys, ScanKey key); @@ -123,26 +121,23 @@ static IndexScanDesc index_beginscan_internal(Relation indexRelation, /* ---------------- * index_open - open an index relation by relation OID * - * Note: we acquire no lock on the index. A lock is not needed when - * simply examining the index reldesc; the index's schema information - * is considered to be protected by the lock that the caller had better - * be holding on the parent relation. Some type of lock should be - * obtained on the index before physically accessing it, however. - * This is handled automatically for most uses by index_beginscan - * and index_endscan for scan cases, or by ExecOpenIndices and - * ExecCloseIndices for update cases. Other callers will need to - * obtain their own locks. + * If lockmode is not "NoLock", the specified kind of lock is + * obtained on the index. (Generally, NoLock should only be + * used if the caller knows it has some appropriate lock on the + * index already.) + * + * An error is raised if the index does not exist. * * This is a convenience routine adapted for indexscan use. * Some callers may prefer to use relation_open directly. * ---------------- */ Relation -index_open(Oid relationId) +index_open(Oid relationId, LOCKMODE lockmode) { Relation r; - r = relation_open(relationId, NoLock); + r = relation_open(relationId, lockmode); if (r->rd_rel->relkind != RELKIND_INDEX) ereport(ERROR, @@ -156,41 +151,26 @@ index_open(Oid relationId) } /* ---------------- - * index_openrv - open an index relation specified - * by a RangeVar node + * index_close - close an index relation * - * As above, but relation is specified by a RangeVar. - * ---------------- - */ -Relation -index_openrv(const RangeVar *relation) -{ - Relation r; - - r = relation_openrv(relation, NoLock); - - if (r->rd_rel->relkind != RELKIND_INDEX) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not an index", - RelationGetRelationName(r)))); - - pgstat_initstats(&r->pgstat_info, r); - - return r; -} - -/* ---------------- - * index_close - close a index relation + * If lockmode is not "NoLock", we then release the specified lock. * - * presently the relcache routines do all the work we need - * to open/close index relations. + * Note that it is often sensible to hold a lock beyond index_close; + * in that case, the lock is released automatically at xact end. * ---------------- */ void -index_close(Relation relation) +index_close(Relation relation, LOCKMODE lockmode) { + LockRelId relid = relation->rd_lockInfo.lockRelId; + + Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES); + + /* The relcache does the real work... */ RelationClose(relation); + + if (lockmode != NoLock) + UnlockRelationId(&relid, lockmode); } /* ---------------- @@ -229,24 +209,18 @@ index_insert(Relation indexRelation, * index_getnext on this scan; index_getnext_indexitem will not use the * heapRelation link (nor the snapshot). However, the caller had better * be holding some kind of lock on the heap relation in any case, to ensure - * no one deletes it (or the index) out from under us. - * - * Most callers should pass need_index_lock = true to cause the index code - * to take AccessShareLock on the index for the duration of the scan. But - * if it is known that a lock is already held on the index, pass false to - * skip taking an unnecessary lock. + * no one deletes it (or the index) out from under us. Caller must also + * be holding a lock on the index. */ IndexScanDesc index_beginscan(Relation heapRelation, Relation indexRelation, - bool need_index_lock, Snapshot snapshot, int nkeys, ScanKey key) { IndexScanDesc scan; - scan = index_beginscan_internal(indexRelation, need_index_lock, - nkeys, key); + scan = index_beginscan_internal(indexRelation, nkeys, key); /* * Save additional parameters into the scandesc. Everything else was set @@ -267,14 +241,12 @@ index_beginscan(Relation heapRelation, */ IndexScanDesc index_beginscan_multi(Relation indexRelation, - bool need_index_lock, Snapshot snapshot, int nkeys, ScanKey key) { IndexScanDesc scan; - scan = index_beginscan_internal(indexRelation, need_index_lock, - nkeys, key); + scan = index_beginscan_internal(indexRelation, nkeys, key); /* * Save additional parameters into the scandesc. Everything else was set @@ -291,33 +263,18 @@ index_beginscan_multi(Relation indexRelation, */ static IndexScanDesc index_beginscan_internal(Relation indexRelation, - bool need_index_lock, int nkeys, ScanKey key) { IndexScanDesc scan; FmgrInfo *procedure; RELATION_CHECKS; - - RelationIncrementReferenceCount(indexRelation); - - /* - * Acquire AccessShareLock for the duration of the scan, unless caller - * says it already has lock on the index. - * - * Note: we could get an SI inval message here and consequently have to - * rebuild the relcache entry. The refcount increment above ensures that - * we will rebuild it and not just flush it... - */ - if (need_index_lock) - LockRelation(indexRelation, AccessShareLock); + GET_REL_PROCEDURE(ambeginscan); /* - * LockRelation can clean rd_aminfo structure, so fill procedure after - * LockRelation + * We hold a reference count to the relcache entry throughout the scan. */ - - GET_REL_PROCEDURE(ambeginscan); + RelationIncrementReferenceCount(indexRelation); /* * Tell the AM to open a scan. @@ -328,9 +285,6 @@ index_beginscan_internal(Relation indexRelation, Int32GetDatum(nkeys), PointerGetDatum(key))); - /* Save flag to tell index_endscan whether to release lock */ - scan->have_lock = need_index_lock; - return scan; } @@ -390,11 +344,7 @@ index_endscan(IndexScanDesc scan) /* End the AM's scan */ FunctionCall1(procedure, PointerGetDatum(scan)); - /* Release index lock and refcount acquired by index_beginscan */ - - if (scan->have_lock) - UnlockRelation(scan->indexRelation, AccessShareLock); - + /* Release index refcount acquired by index_beginscan */ RelationDecrementReferenceCount(scan->indexRelation); /* Release the scan data structure itself */ |