diff --git a/contrib/userlock/user_locks.c b/contrib/userlock/user_locks.c index c27dc253d8..394a4fe190 100644 --- a/contrib/userlock/user_locks.c +++ b/contrib/userlock/user_locks.c @@ -35,8 +35,7 @@ user_lock(uint32 id1, uint32 id2, LOCKMODE lockmode) SET_LOCKTAG_USERLOCK(tag, id1, id2); - return (LockAcquire(&tag, false, - lockmode, true, true) != LOCKACQUIRE_NOT_AVAIL); + return (LockAcquire(&tag, lockmode, true, true) != LOCKACQUIRE_NOT_AVAIL); } int diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index dfb6348e1d..80ed7d829b 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.128 2006/07/27 08:30:41 petere Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.129 2006/07/31 20:08:55 tgl Exp $ --> <!-- Documentation of the system catalogs, directed toward PostgreSQL developers --> @@ -401,13 +401,6 @@ <entry>Can index storage data type differ from column data type?</entry> </row> - <row> - <entry><structfield>amconcurrent</structfield></entry> - <entry><type>bool</type></entry> - <entry></entry> - <entry>Does the access method support concurrent updates?</entry> - </row> - <row> <entry><structfield>amclusterable</structfield></entry> <entry><type>bool</type></entry> diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml index 7febd0c907..1afa120766 100644 --- a/doc/src/sgml/indexam.sgml +++ b/doc/src/sgml/indexam.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.15 2006/07/03 22:45:36 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.16 2006/07/31 20:08:59 tgl Exp $ --> <chapter id="indexam"> <title>Index Access Method Interface Definition</title> @@ -94,8 +94,7 @@ <para> Some of the flag columns of <structname>pg_am</structname> have nonobvious implications. The requirements of <structfield>amcanunique</structfield> - are discussed in <xref linkend="index-unique-checks">, and those of - <structfield>amconcurrent</structfield> in <xref linkend="index-locking">. + are discussed in <xref linkend="index-unique-checks">. The <structfield>amcanmulticol</structfield> flag asserts that the access method supports multicolumn indexes, while <structfield>amoptionalkey</structfield> asserts that it allows scans @@ -474,11 +473,7 @@ amrestrpos (IndexScanDesc scan); a concurrent delete may or may not be reflected in the results of a scan. What is important is that insertions or deletions not cause the scan to miss or multiply return entries that were not themselves being inserted or - deleted. (For an index type that does not set - <structname>pg_am</>.<structfield>amconcurrent</>, it is sufficient to - handle these cases for insertions or deletions performed by the same - backend that's doing the scan. But when <structfield>amconcurrent</> is - true, insertions or deletions from other backends must be handled as well.) + deleted. </para> <para> @@ -506,31 +501,16 @@ amrestrpos (IndexScanDesc scan); <title>Index Locking Considerations</title> <para> - An index access method can choose whether it supports concurrent updates - of the index by multiple processes. If the method's - <structname>pg_am</>.<structfield>amconcurrent</> flag is true, then - the core <productname>PostgreSQL</productname> system obtains + Index access methods must handle concurrent updates + of the index by multiple processes. + The core <productname>PostgreSQL</productname> system obtains <literal>AccessShareLock</> on the index during an index scan, and - <literal>RowExclusiveLock</> when updating the index. Since these lock + <literal>RowExclusiveLock</> when updating the index (including plain + <command>VACUUM</>). Since these lock types do not conflict, the access method is responsible for handling any fine-grained locking it may need. An exclusive lock on the index as a whole - will be taken only during index creation, destruction, or - <literal>REINDEX</>. When <structfield>amconcurrent</> is false, - <productname>PostgreSQL</productname> still obtains - <literal>AccessShareLock</> during index scans, but it obtains - <literal>AccessExclusiveLock</> during any update. This ensures that - updaters have sole use of the index. Note that this implicitly assumes - that index scans are read-only; an access method that might modify the - index during a scan will still have to do its own locking to handle the - case of concurrent scans. - </para> - - <para> - Recall that a backend's own locks never conflict; therefore, even a - non-concurrent index type must be prepared to handle the case where - a backend is inserting or deleting entries in an index that it is itself - scanning. (This is of course necessary to support an <command>UPDATE</> - that uses the index to find the rows to be updated.) + will be taken only during index creation, destruction, + <command>REINDEX</>, or <command>VACUUM FULL</>. </para> <para> @@ -567,7 +547,7 @@ amrestrpos (IndexScanDesc scan); </listitem> <listitem> <para> - For concurrent index types, an index scan must maintain a pin + An index scan must maintain a pin on the index page holding the item last returned by <function>amgettuple</>, and <function>ambulkdelete</> cannot delete entries from pages that are pinned by other backends. The need @@ -576,11 +556,10 @@ amrestrpos (IndexScanDesc scan); </listitem> </itemizedlist> - If an index is concurrent then it is possible for an index reader to + Without the third rule, it is possible for an index reader to see an index entry just before it is removed by <command>VACUUM</>, and then to arrive at the corresponding heap entry after that was removed by - <command>VACUUM</>. (With a nonconcurrent index, this is not possible - because of the conflicting index-level locks that will be taken out.) + <command>VACUUM</>. This creates no serious problems if that item number is still unused when the reader reaches it, since an empty item slot will be ignored by <function>heap_fetch()</>. But what if a diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c index c6551939a2..2bc80a2643 100644 --- a/src/backend/access/gin/ginvacuum.c +++ b/src/backend/access/gin/ginvacuum.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.4 2006/07/14 14:52:16 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.5 2006/07/31 20:08:59 tgl Exp $ *------------------------------------------------------------------------- */ @@ -572,7 +572,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) { IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0); IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1); Relation index = info->index; - bool needLock = !RELATION_IS_LOCAL(index); + bool needLock; BlockNumber npages, blkno; BlockNumber nFreePages, @@ -591,10 +591,14 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) { */ stats->num_index_tuples = info->num_heap_tuples; - if (info->vacuum_full) { - LockRelation(index, AccessExclusiveLock); + /* + * If vacuum full, we already have exclusive lock on the index. + * Otherwise, need lock unless it's local to this backend. + */ + if (info->vacuum_full) needLock = false; - } + else + needLock = !RELATION_IS_LOCAL(index); if (needLock) LockRelationForExtension(index, ExclusiveLock); @@ -653,9 +657,6 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) { if (needLock) UnlockRelationForExtension(index, ExclusiveLock); - if (info->vacuum_full) - UnlockRelation(index, AccessExclusiveLock); - PG_RETURN_POINTER(stats); } diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c index e3bff5147c..37b5631b28 100644 --- a/src/backend/access/gist/gistvacuum.c +++ b/src/backend/access/gist/gistvacuum.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.25 2006/07/14 14:52:16 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.26 2006/07/31 20:08:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -517,7 +517,7 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) GistVacuum gv; ArrayTuple res; - LockRelation(rel, AccessExclusiveLock); + /* note: vacuum.c already acquired AccessExclusiveLock on index */ gv.index = rel; initGISTstate(&(gv.giststate), rel); @@ -543,8 +543,12 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) (errmsg("index \"%s\" needs VACUUM FULL or REINDEX to finish crash recovery", RelationGetRelationName(rel)))); + /* + * If vacuum full, we already have exclusive lock on the index. + * Otherwise, need lock unless it's local to this backend. + */ if (info->vacuum_full) - needLock = false; /* relation locked with AccessExclusiveLock */ + needLock = false; else needLock = !RELATION_IS_LOCAL(rel); @@ -613,9 +617,6 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) if (needLock) UnlockRelationForExtension(rel, ExclusiveLock); - if (info->vacuum_full) - UnlockRelation(rel, AccessExclusiveLock); - PG_RETURN_POINTER(stats); } diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 91460410bf..f29407e8e5 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.217 2006/07/14 14:52:17 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.218 2006/07/31 20:08:59 tgl Exp $ * * * INTERFACE ROUTINES @@ -51,6 +51,7 @@ #include "pgstat.h" #include "storage/procarray.h" #include "utils/inval.h" +#include "utils/lsyscache.h" #include "utils/relcache.h" @@ -687,15 +688,16 @@ relation_open(Oid relationId, LOCKMODE lockmode) Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES); + /* Get the lock before trying to open the relcache entry */ + if (lockmode != NoLock) + LockRelationOid(relationId, lockmode); + /* The relcache does all the real work... */ r = RelationIdGetRelation(relationId); if (!RelationIsValid(r)) elog(ERROR, "could not open relation with OID %u", relationId); - if (lockmode != NoLock) - LockRelation(r, lockmode); - return r; } @@ -713,26 +715,38 @@ conditional_relation_open(Oid relationId, LOCKMODE lockmode, bool nowait) Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES); + /* Get the lock before trying to open the relcache entry */ + if (lockmode != NoLock) + { + if (nowait) + { + if (!ConditionalLockRelationOid(relationId, lockmode)) + { + /* try to throw error by name; relation could be deleted... */ + char *relname = get_rel_name(relationId); + + if (relname) + ereport(ERROR, + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on relation \"%s\"", + relname))); + else + ereport(ERROR, + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on relation with OID %u", + relationId))); + } + } + else + LockRelationOid(relationId, lockmode); + } + /* The relcache does all the real work... */ r = RelationIdGetRelation(relationId); if (!RelationIsValid(r)) elog(ERROR, "could not open relation with OID %u", relationId); - if (lockmode != NoLock) - { - if (nowait) - { - if (!ConditionalLockRelation(r, lockmode)) - ereport(ERROR, - (errcode(ERRCODE_LOCK_NOT_AVAILABLE), - errmsg("could not obtain lock on relation \"%s\"", - RelationGetRelationName(r)))); - } - else - LockRelation(r, lockmode); - } - return r; } @@ -749,12 +763,12 @@ relation_openrv(const RangeVar *relation, LOCKMODE lockmode) /* * Check for shared-cache-inval messages before trying to open the - * relation. This is needed to cover the case where the name identifies a - * rel that has been dropped and recreated since the start of our + * relation. This is needed to cover the case where the name identifies + * a rel that has been dropped and recreated since the start of our * transaction: if we don't flush the old syscache entry then we'll latch - * onto that entry and suffer an error when we do LockRelation. Note that - * relation_open does not need to do this, since a relation's OID never - * changes. + * onto that entry and suffer an error when we do RelationIdGetRelation. + * Note that relation_open does not need to do this, since a relation's + * OID never changes. * * We skip this if asked for NoLock, on the assumption that the caller has * already ensured some appropriate lock is held. @@ -772,7 +786,7 @@ relation_openrv(const RangeVar *relation, LOCKMODE lockmode) /* ---------------- * relation_close - close any relation * - * If lockmode is not "NoLock", we first release the specified lock. + * If lockmode is not "NoLock", we then release the specified lock. * * Note that it is often sensible to hold a lock beyond relation_close; * in that case, the lock is released automatically at xact end. @@ -781,13 +795,15 @@ relation_openrv(const RangeVar *relation, LOCKMODE lockmode) void relation_close(Relation relation, LOCKMODE lockmode) { - Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES); + LockRelId relid = relation->rd_lockInfo.lockRelId; - if (lockmode != NoLock) - UnlockRelation(relation, lockmode); + Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES); /* The relcache does the real work... */ RelationClose(relation); + + if (lockmode != NoLock) + UnlockRelationId(&relid, lockmode); } diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c index 11adf165c4..43ca366f0a 100644 --- a/src/backend/access/heap/tuptoaster.c +++ b/src/backend/access/heap/tuptoaster.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.62 2006/07/14 14:52:17 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.63 2006/07/31 20:08:59 tgl Exp $ * * * INTERFACE ROUTINES @@ -1004,7 +1004,7 @@ toast_save_datum(Relation rel, Datum value) */ toastrel = heap_open(rel->rd_rel->reltoastrelid, RowExclusiveLock); toasttupDesc = toastrel->rd_att; - toastidx = index_open(toastrel->rd_rel->reltoastidxid); + toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock); /* * Create the varattrib reference @@ -1043,12 +1043,6 @@ toast_save_datum(Relation rel, Datum value) data_p = VARATT_DATA(value); data_todo = VARATT_SIZE(value) - VARHDRSZ; - /* - * We must explicitly lock the toast index because we aren't using an - * index scan here. - */ - LockRelation(toastidx, RowExclusiveLock); - /* * Split up the item into chunks */ @@ -1098,8 +1092,7 @@ toast_save_datum(Relation rel, Datum value) /* * Done - close toast relation and return the reference */ - UnlockRelation(toastidx, RowExclusiveLock); - index_close(toastidx); + index_close(toastidx, RowExclusiveLock); heap_close(toastrel, RowExclusiveLock); return PointerGetDatum(result); @@ -1130,7 +1123,7 @@ toast_delete_datum(Relation rel, Datum value) */ toastrel = heap_open(attr->va_content.va_external.va_toastrelid, RowExclusiveLock); - toastidx = index_open(toastrel->rd_rel->reltoastidxid); + toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock); /* * Setup a scan key to fetch from the index by va_valueid (we don't @@ -1144,7 +1137,7 @@ toast_delete_datum(Relation rel, Datum value) /* * Find the chunks by index */ - toastscan = index_beginscan(toastrel, toastidx, true, + toastscan = index_beginscan(toastrel, toastidx, SnapshotToast, 1, &toastkey); while ((toasttup = index_getnext(toastscan, ForwardScanDirection)) != NULL) { @@ -1158,7 +1151,7 @@ toast_delete_datum(Relation rel, Datum value) * End scan and close relations */ index_endscan(toastscan); - index_close(toastidx); + index_close(toastidx, RowExclusiveLock); heap_close(toastrel, RowExclusiveLock); } @@ -1202,7 +1195,7 @@ toast_fetch_datum(varattrib *attr) toastrel = heap_open(attr->va_content.va_external.va_toastrelid, AccessShareLock); toasttupDesc = toastrel->rd_att; - toastidx = index_open(toastrel->rd_rel->reltoastidxid); + toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock); /* * Setup a scan key to fetch from the index by va_valueid @@ -1221,7 +1214,7 @@ toast_fetch_datum(varattrib *attr) */ nextidx = 0; - toastscan = index_beginscan(toastrel, toastidx, true, + toastscan = index_beginscan(toastrel, toastidx, SnapshotToast, 1, &toastkey); while ((ttup = index_getnext(toastscan, ForwardScanDirection)) != NULL) { @@ -1282,7 +1275,7 @@ toast_fetch_datum(varattrib *attr) * End scan and close relations */ index_endscan(toastscan); - index_close(toastidx); + index_close(toastidx, AccessShareLock); heap_close(toastrel, AccessShareLock); return result; @@ -1355,7 +1348,7 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length) toastrel = heap_open(attr->va_content.va_external.va_toastrelid, AccessShareLock); toasttupDesc = toastrel->rd_att; - toastidx = index_open(toastrel->rd_rel->reltoastidxid); + toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock); /* * Setup a scan key to fetch from the index. This is either two keys or @@ -1396,7 +1389,7 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length) * The index is on (valueid, chunkidx) so they will come in order */ nextidx = startchunk; - toastscan = index_beginscan(toastrel, toastidx, true, + toastscan = index_beginscan(toastrel, toastidx, SnapshotToast, nscankeys, toastkey); while ((ttup = index_getnext(toastscan, ForwardScanDirection)) != NULL) { @@ -1461,7 +1454,7 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length) * End scan and close relations */ index_endscan(toastscan); - index_close(toastidx); + index_close(toastidx, AccessShareLock); heap_close(toastrel, AccessShareLock); return result; diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c index 399386c856..347d2b5365 100644 --- a/src/backend/access/index/genam.c +++ b/src/backend/access/index/genam.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.57 2006/07/03 22:45:37 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.58 2006/07/31 20:08:59 tgl Exp $ * * NOTES * many of the old access method routines have been turned into @@ -87,7 +87,6 @@ RelationGetIndexScan(Relation indexRelation, scan->keyData = NULL; scan->is_multiscan = false; /* caller may change this */ - scan->have_lock = false; /* ditto */ scan->kill_prior_tuple = false; scan->ignore_killed_tuples = true; /* default setting */ @@ -182,7 +181,7 @@ systable_beginscan(Relation heapRelation, if (indexOK && !IgnoreSystemIndexes && !ReindexIsProcessingIndex(indexId)) - irel = index_open(indexId); + irel = index_open(indexId, AccessShareLock); else irel = NULL; @@ -207,7 +206,7 @@ systable_beginscan(Relation heapRelation, key[i].sk_attno = i + 1; } - sysscan->iscan = index_beginscan(heapRelation, irel, true, + sysscan->iscan = index_beginscan(heapRelation, irel, snapshot, nkeys, key); sysscan->scan = NULL; } @@ -253,7 +252,7 @@ systable_endscan(SysScanDesc sysscan) if (sysscan->irel) { index_endscan(sysscan->iscan); - index_close(sysscan->irel); + index_close(sysscan->irel, AccessShareLock); } else heap_endscan(sysscan->scan); diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 900b34263d..2663876f49 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,34 +263,19 @@ 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); - - /* - * LockRelation can clean rd_aminfo structure, so fill procedure after - * LockRelation - */ - GET_REL_PROCEDURE(ambeginscan); + /* + * We hold a reference count to the relcache entry throughout the scan. + */ + 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 */ diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 9399b6d052..08189bf07b 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.222 2006/07/31 01:16:36 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.223 2006/07/31 20:09:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1239,12 +1239,13 @@ build_indices(void) Relation heap; Relation ind; + /* need not bother with locks during bootstrap */ heap = heap_open(ILHead->il_heap, NoLock); - ind = index_open(ILHead->il_ind); + ind = index_open(ILHead->il_ind, NoLock); index_build(heap, ind, ILHead->il_info, false); - index_close(ind); + index_close(ind, NoLock); heap_close(heap, NoLock); } } diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c index a7b50df950..5d99acafc9 100644 --- a/src/backend/catalog/catalog.c +++ b/src/backend/catalog/catalog.c @@ -1,7 +1,8 @@ /*------------------------------------------------------------------------- * * catalog.c - * routines concerned with catalog naming conventions + * routines concerned with catalog naming conventions and other + * bits of hard-wired knowledge * * * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group @@ -9,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.66 2006/03/05 15:58:22 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.67 2006/07/31 20:09:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,8 +23,16 @@ #include "access/genam.h" #include "access/transam.h" #include "catalog/catalog.h" +#include "catalog/indexing.h" +#include "catalog/pg_auth_members.h" +#include "catalog/pg_authid.h" +#include "catalog/pg_database.h" #include "catalog/pg_namespace.h" +#include "catalog/pg_pltemplate.h" +#include "catalog/pg_shdepend.h" +#include "catalog/pg_shdescription.h" #include "catalog/pg_tablespace.h" +#include "catalog/toasting.h" #include "miscadmin.h" #include "storage/fd.h" #include "utils/fmgroids.h" @@ -217,6 +226,64 @@ IsReservedName(const char *name) } +/* + * IsSharedRelation + * Given the OID of a relation, determine whether it's supposed to be + * shared across an entire database cluster. + * + * Hard-wiring this list is pretty grotty, but we really need it so that + * we can compute the locktag for a relation (and then lock it) without + * having already read its pg_class entry. If we try to retrieve relisshared + * from pg_class with no pre-existing lock, there is a race condition against + * anyone who is concurrently committing a change to the pg_class entry: + * since we read system catalog entries under SnapshotNow, it's possible + * that both the old and new versions of the row are invalid at the instants + * we scan them. We fix this by insisting that updaters of a pg_class + * row must hold exclusive lock on the corresponding rel, and that users + * of a relation must hold at least AccessShareLock on the rel *before* + * trying to open its relcache entry. But to lock a rel, you have to + * know if it's shared. Fortunately, the set of shared relations is + * fairly static, so a hand-maintained list of their OIDs isn't completely + * impractical. + */ +bool +IsSharedRelation(Oid relationId) +{ + /* These are the shared catalogs (look for BKI_SHARED_RELATION) */ + if (relationId == AuthIdRelationId || + relationId == AuthMemRelationId || + relationId == DatabaseRelationId || + relationId == PLTemplateRelationId || + relationId == SharedDescriptionRelationId || + relationId == SharedDependRelationId || + relationId == TableSpaceRelationId) + return true; + /* These are their indexes (see indexing.h) */ + if (relationId == AuthIdRolnameIndexId || + relationId == AuthIdOidIndexId || + relationId == AuthMemRoleMemIndexId || + relationId == AuthMemMemRoleIndexId || + relationId == DatabaseNameIndexId || + relationId == DatabaseOidIndexId || + relationId == PLTemplateNameIndexId || + relationId == SharedDescriptionObjIndexId || + relationId == SharedDependDependerIndexId || + relationId == SharedDependReferenceIndexId || + relationId == TablespaceOidIndexId || + relationId == TablespaceNameIndexId) + return true; + /* These are their toast tables and toast indexes (see toasting.h) */ + if (relationId == PgAuthidToastTable || + relationId == PgAuthidToastIndex || + relationId == PgDatabaseToastTable || + relationId == PgDatabaseToastIndex || + relationId == PgShdescriptionToastTable || + relationId == PgShdescriptionToastIndex) + return true; + return false; +} + + /* * GetNewOid * Generate a new OID that is unique within the given relation. @@ -271,9 +338,9 @@ GetNewOid(Relation relation) } /* Otherwise, use the index to find a nonconflicting OID */ - indexrel = index_open(oidIndex); + indexrel = index_open(oidIndex, AccessShareLock); newOid = GetNewOidWithIndex(relation, indexrel); - index_close(indexrel); + index_close(indexrel, AccessShareLock); return newOid; } @@ -309,7 +376,7 @@ GetNewOidWithIndex(Relation relation, Relation indexrel) ObjectIdGetDatum(newOid)); /* see notes above about using SnapshotDirty */ - scan = index_beginscan(relation, indexrel, true, + scan = index_beginscan(relation, indexrel, SnapshotDirty, 1, &key); collides = HeapTupleIsValid(index_getnext(scan, ForwardScanDirection)); diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 361f680087..922a0cebf7 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.310 2006/07/31 01:16:36 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.311 2006/07/31 20:09:00 tgl Exp $ * * * INTERFACE ROUTINES @@ -1971,24 +1971,17 @@ RemoveStatistics(Oid relid, AttrNumber attnum) /* - * RelationTruncateIndexes - truncate all - * indexes associated with the heap relation to zero tuples. + * RelationTruncateIndexes - truncate all indexes associated + * with the heap relation to zero tuples. * * The routine will truncate and then reconstruct the indexes on - * the relation specified by the heapId parameter. + * the specified relation. Caller must hold exclusive lock on rel. */ static void -RelationTruncateIndexes(Oid heapId) +RelationTruncateIndexes(Relation heapRelation) { - Relation heapRelation; ListCell *indlist; - /* - * Open the heap rel. We need grab no lock because we assume - * heap_truncate is holding an exclusive lock on the heap rel. - */ - heapRelation = heap_open(heapId, NoLock); - /* Ask the relcache to produce a list of the indexes of the rel */ foreach(indlist, RelationGetIndexList(heapRelation)) { @@ -1996,11 +1989,8 @@ RelationTruncateIndexes(Oid heapId) Relation currentIndex; IndexInfo *indexInfo; - /* Open the index relation */ - currentIndex = index_open(indexId); - - /* Obtain exclusive lock on it, just to be sure */ - LockRelation(currentIndex, AccessExclusiveLock); + /* Open the index relation; use exclusive lock, just to be sure */ + currentIndex = index_open(indexId, AccessExclusiveLock); /* Fetch info needed for index_build */ indexInfo = BuildIndexInfo(currentIndex); @@ -2013,11 +2003,8 @@ RelationTruncateIndexes(Oid heapId) index_build(heapRelation, currentIndex, indexInfo, false); /* We're done with this index */ - index_close(currentIndex); + index_close(currentIndex, NoLock); } - - /* And now done with the heap; but keep lock until commit */ - heap_close(heapRelation, NoLock); } /* @@ -2066,7 +2053,7 @@ heap_truncate(List *relids) RelationTruncate(rel, 0); /* If this relation has indexes, truncate the indexes too */ - RelationTruncateIndexes(RelationGetRelid(rel)); + RelationTruncateIndexes(rel); /* * Close the relation, but keep exclusive lock on it until commit. diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 092c9dcda1..0da209ff21 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.271 2006/07/31 01:16:36 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.272 2006/07/31 20:09:00 tgl Exp $ * * * INTERFACE ROUTINES @@ -483,12 +483,8 @@ index_create(Oid heapRelationId, /* * We cannot allow indexing a shared relation after initdb (because * there's no way to make the entry in other databases' pg_class). - * Unfortunately we can't distinguish initdb from a manually started - * standalone backend (toasting of shared rels happens after the bootstrap - * phase, so checking IsBootstrapProcessingMode() won't work). However, - * we can at least prevent this mistake under normal multi-user operation. */ - if (shared_relation && IsUnderPostmaster) + if (shared_relation && !IsBootstrapProcessingMode()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("shared indexes cannot be created after initdb"))); @@ -753,7 +749,7 @@ index_create(Oid heapRelationId, * the exclusive lock on the index that we acquired above, until end of * transaction. */ - index_close(indexRelation); + index_close(indexRelation, NoLock); heap_close(heapRelation, NoLock); return indexRelationId; @@ -789,8 +785,7 @@ index_drop(Oid indexId) heapId = IndexGetRelation(indexId); userHeapRelation = heap_open(heapId, AccessExclusiveLock); - userIndexRelation = index_open(indexId); - LockRelation(userIndexRelation, AccessExclusiveLock); + userIndexRelation = index_open(indexId, AccessExclusiveLock); /* * Schedule physical removal of the file @@ -804,7 +799,7 @@ index_drop(Oid indexId) * try to rebuild it while we're deleting catalog entries. We keep the * lock though. */ - index_close(userIndexRelation); + index_close(userIndexRelation, NoLock); RelationForgetRelation(indexId); @@ -982,11 +977,11 @@ FormIndexDatum(IndexInfo *indexInfo, /* - * index_update_stats --- update pg_class entry after CREATE INDEX + * index_update_stats --- update pg_class entry after CREATE INDEX or REINDEX * * This routine updates the pg_class row of either an index or its parent - * relation after CREATE INDEX. Its rather bizarre API is designed to - * ensure we can do all the necessary work in just one update. + * relation after CREATE INDEX or REINDEX. Its rather bizarre API is designed + * to ensure we can do all the necessary work in just one update. * * hasindex: set relhasindex to this value * isprimary: if true, set relhaspkey true; else no change @@ -1013,33 +1008,50 @@ index_update_stats(Relation rel, bool hasindex, bool isprimary, Relation pg_class; HeapTuple tuple; Form_pg_class rd_rel; - bool in_place_upd; bool dirty; /* - * Find the tuple to update in pg_class. Normally we make a copy of the - * tuple using the syscache, modify it, and apply heap_update. But in - * bootstrap mode we can't use heap_update, so we use a nontransactional - * update, ie, overwrite the tuple in-place. + * We always update the pg_class row using a non-transactional, + * overwrite-in-place update. There are several reasons for this: * - * We also must use an in-place update if reindexing pg_class itself, - * because the target index may presently not be part of the set of - * indexes that CatalogUpdateIndexes would update (see reindex_relation). + * 1. In bootstrap mode, we have no choice --- UPDATE wouldn't work. + * + * 2. We could be reindexing pg_class itself, in which case we can't + * move its pg_class row because CatalogUpdateIndexes might not know + * about all the indexes yet (see reindex_relation). + * + * 3. Because we execute CREATE INDEX with just share lock on the parent + * rel (to allow concurrent index creations), an ordinary update could + * suffer a tuple-concurrently-updated failure against another CREATE + * INDEX committing at about the same time. We can avoid that by having + * them both do nontransactional updates (we assume they will both be + * trying to change the pg_class row to the same thing, so it doesn't + * matter which goes first). + * + * 4. Even with just a single CREATE INDEX, there's a risk factor because + * someone else might be trying to open the rel while we commit, and this + * creates a race condition as to whether he will see both or neither of + * the pg_class row versions as valid. Again, a non-transactional update + * avoids the risk. It is indeterminate which state of the row the other + * process will see, but it doesn't matter (if he's only taking + * AccessShareLock, then it's not critical that he see relhasindex true). + * + * It is safe to use a non-transactional update even though our + * transaction could still fail before committing. Setting relhasindex + * true is safe even if there are no indexes (VACUUM will eventually fix + * it), and of course the relpages and reltuples counts are correct (or + * at least more so than the old values) regardless. */ + pg_class = heap_open(RelationRelationId, RowExclusiveLock); - in_place_upd = IsBootstrapProcessingMode() || - ReindexIsProcessingHeap(RelationRelationId); - -restart: - - if (!in_place_upd) - { - tuple = SearchSysCacheCopy(RELOID, - ObjectIdGetDatum(relid), - 0, 0, 0); - } - else + /* + * Make a copy of the tuple to update. Normally we use the syscache, + * but we can't rely on that during bootstrap or while reindexing + * pg_class itself. + */ + if (IsBootstrapProcessingMode() || + ReindexIsProcessingHeap(RelationRelationId)) { /* don't assume syscache will work */ HeapScanDesc pg_class_scan; @@ -1055,6 +1067,13 @@ restart: tuple = heap_copytuple(tuple); heap_endscan(pg_class_scan); } + else + { + /* normal case, use syscache */ + tuple = SearchSysCacheCopy(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + } if (!HeapTupleIsValid(tuple)) elog(ERROR, "could not find tuple for relation %u", relid); @@ -1101,53 +1120,8 @@ restart: */ if (dirty) { - if (in_place_upd) - { - heap_inplace_update(pg_class, tuple); - } - else - { - /* - * Because PG allows concurrent CREATE INDEX commands, it's - * possible that someone else tries to update the pg_class - * row at about the same time we do. Hence, instead of using - * simple_heap_update(), we must use full heap_update() and - * cope with HeapTupleUpdated result. If we see that, just - * go back and try the whole update again. - */ - HTSU_Result result; - ItemPointerData update_ctid; - TransactionId update_xmax; - - result = heap_update(pg_class, &tuple->t_self, tuple, - &update_ctid, &update_xmax, - GetCurrentCommandId(), InvalidSnapshot, - true /* wait for commit */ ); - switch (result) - { - case HeapTupleSelfUpdated: - /* Tuple was already updated in current command? */ - elog(ERROR, "tuple already updated by self"); - break; - - case HeapTupleMayBeUpdated: - /* done successfully */ - break; - - case HeapTupleUpdated: - heap_freetuple(tuple); - /* Must do CCI so we can see the updated tuple */ - CommandCounterIncrement(); - goto restart; - - default: - elog(ERROR, "unrecognized heap_update status: %u", result); - break; - } - - /* Keep the catalog indexes up to date */ - CatalogUpdateIndexes(pg_class, tuple); - } + heap_inplace_update(pg_class, tuple); + /* the above sends a cache inval message */ } else { @@ -1571,8 +1545,7 @@ reindex_index(Oid indexId) * Open the target index relation and get an exclusive lock on it, to * ensure that no one else is touching this particular index. */ - iRel = index_open(indexId); - LockRelation(iRel, AccessExclusiveLock); + iRel = index_open(indexId, AccessExclusiveLock); /* * If it's a shared index, we must do inplace processing (because we have @@ -1628,7 +1601,7 @@ reindex_index(Oid indexId) ResetReindexProcessing(); /* Close rels, but keep locks */ - index_close(iRel); + index_close(iRel, NoLock); heap_close(heapRelation, NoLock); } diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index e16afa0559..f0cbaefd60 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.151 2006/07/31 01:16:37 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.152 2006/07/31 20:09:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -314,8 +314,7 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck) { Relation OldIndex; - OldIndex = index_open(indexOid); - LockRelation(OldIndex, AccessExclusiveLock); + OldIndex = index_open(indexOid, AccessExclusiveLock); /* * Check that index is in fact an index on the given relation @@ -406,7 +405,7 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck) errmsg("cannot cluster temporary tables of other sessions"))); /* Drop relcache refcnt on OldIndex, but keep lock */ - index_close(OldIndex); + index_close(OldIndex, NoLock); } /* @@ -649,7 +648,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex) */ NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock); OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock); - OldIndex = index_open(OIDOldIndex); + OldIndex = index_open(OIDOldIndex, AccessExclusiveLock); /* * Their tuple descriptors should be exactly alike, but here we only need @@ -669,7 +668,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex) * Scan through the OldHeap on the OldIndex and copy each tuple into the * NewHeap. */ - scan = index_beginscan(OldHeap, OldIndex, true, + scan = index_beginscan(OldHeap, OldIndex, SnapshotNow, 0, (ScanKey) NULL); while ((tuple = index_getnext(scan, ForwardScanDirection)) != NULL) @@ -724,7 +723,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex) pfree(values); pfree(nulls); - index_close(OldIndex); + index_close(OldIndex, NoLock); heap_close(OldHeap, NoLock); heap_close(NewHeap, NoLock); } diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index a3b35a640e..6154a4ed3d 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.137 2006/07/14 14:52:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.138 2006/07/31 20:09:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -79,7 +79,7 @@ static SeqTable seqtab = NULL; /* Head of list of SeqTable items */ static SeqTableData *last_used_seq = NULL; static int64 nextval_internal(Oid relid); -static void acquire_share_lock(Relation seqrel, SeqTable seq); +static Relation open_share_lock(SeqTable seq); static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel); static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf); static void init_params(List *options, Form_pg_sequence new, bool isInit); @@ -650,8 +650,7 @@ lastval(PG_FUNCTION_ARGS) (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("lastval is not yet defined in this session"))); - seqrel = relation_open(last_used_seq->relid, NoLock); - acquire_share_lock(seqrel, last_used_seq); + seqrel = open_share_lock(last_used_seq); /* nextval() must have already been called for this sequence */ Assert(last_used_seq->increment != 0); @@ -802,16 +801,19 @@ setval3_oid(PG_FUNCTION_ARGS) /* + * Open the sequence and acquire AccessShareLock if needed + * * If we haven't touched the sequence already in this transaction, * we need to acquire AccessShareLock. We arrange for the lock to * be owned by the top transaction, so that we don't need to do it * more than once per xact. */ -static void -acquire_share_lock(Relation seqrel, SeqTable seq) +static Relation +open_share_lock(SeqTable seq) { TransactionId thisxid = GetTopTransactionId(); + /* Get the lock if not already held in this xact */ if (seq->xid != thisxid) { ResourceOwner currentOwner; @@ -820,7 +822,7 @@ acquire_share_lock(Relation seqrel, SeqTable seq) PG_TRY(); { CurrentResourceOwner = TopTransactionResourceOwner; - LockRelation(seqrel, AccessShareLock); + LockRelationOid(seq->relid, AccessShareLock); } PG_CATCH(); { @@ -831,9 +833,12 @@ acquire_share_lock(Relation seqrel, SeqTable seq) PG_END_TRY(); CurrentResourceOwner = currentOwner; - /* Flag that we have a lock in the current xact. */ + /* Flag that we have a lock in the current xact */ seq->xid = thisxid; } + + /* We now know we have AccessShareLock, and can safely open the rel */ + return relation_open(seq->relid, NoLock); } /* @@ -843,19 +848,8 @@ acquire_share_lock(Relation seqrel, SeqTable seq) static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel) { + SeqTable elm; Relation seqrel; - volatile SeqTable elm; - - /* - * Open the sequence relation. - */ - seqrel = relation_open(relid, NoLock); - - if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a sequence", - RelationGetRelationName(seqrel)))); /* Look to see if we already have a seqtable entry for relation */ for (elm = seqtab; elm != NULL; elm = elm->next) @@ -890,7 +884,16 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel) seqtab = elm; } - acquire_share_lock(seqrel, elm); + /* + * Open the sequence relation. + */ + seqrel = open_share_lock(elm); + + if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", + RelationGetRelationName(seqrel)))); *p_elm = elm; *p_rel = seqrel; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 53382ff3d4..3b408b411a 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.197 2006/07/31 01:16:37 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.198 2006/07/31 20:09:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -5863,7 +5863,10 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace) HeapTuple tuple; Form_pg_class rd_rel; - rel = relation_open(tableOid, NoLock); + /* + * Need lock here in case we are recursing to toast table or index + */ + rel = relation_open(tableOid, AccessExclusiveLock); /* * We can never allow moving of shared or nailed-in-cache relations, diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 158e783c57..13e7cab721 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.204 2006/07/14 14:52:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.205 2006/07/31 20:09:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,7 +36,6 @@ #include "utils/inval.h" #include "utils/lsyscache.h" #include "utils/memutils.h" -#include "utils/relcache.h" #include "utils/syscache.h" @@ -2986,7 +2985,6 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt) while (HeapTupleIsValid(htup = systable_getnext(tgscan))) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup); - Relation constraintRel; Oid constraintNamespaceId; /* @@ -3010,13 +3008,9 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt) pg_trigger->tgfoid == F_RI_FKEY_SETNULL_DEL || pg_trigger->tgfoid == F_RI_FKEY_SETDEFAULT_UPD || pg_trigger->tgfoid == F_RI_FKEY_SETDEFAULT_DEL) - { - constraintRel = RelationIdGetRelation(pg_trigger->tgconstrrelid); - } else { - constraintRel = RelationIdGetRelation(pg_trigger->tgrelid); - } - constraintNamespaceId = RelationGetNamespace(constraintRel); - RelationClose(constraintRel); + constraintNamespaceId = get_rel_namespace(pg_trigger->tgconstrrelid); + else + constraintNamespaceId = get_rel_namespace(pg_trigger->tgrelid); /* * If this constraint is not in the schema we're diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 1304e68681..6dfa6296d5 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.95 2006/07/14 14:52:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.96 2006/07/31 20:09:00 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -1625,6 +1625,8 @@ get_rels_with_domain(Oid domainOid, LOCKMODE lockmode) SysScanDesc depScan; HeapTuple depTup; + Assert(lockmode != NoLock); + /* * We scan pg_depend to find those things that depend on the domain. (We * assume we can ignore refobjsubid for a domain.) diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index eb0fce72ed..c21be2f478 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.336 2006/07/30 02:07:18 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.337 2006/07/31 20:09:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1138,7 +1138,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind) * same process. */ onerelid = onerel->rd_lockInfo.lockRelId; - LockRelationForSession(&onerelid, onerel->rd_istemp, lmode); + LockRelationIdForSession(&onerelid, lmode); /* * Remember the relation's TOAST relation for later @@ -1175,7 +1175,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind) /* * Now release the session-level lock on the master table. */ - UnlockRelationForSession(&onerelid, lmode); + UnlockRelationIdForSession(&onerelid, lmode); return; } @@ -3476,6 +3476,8 @@ vac_open_indexes(Relation relation, LOCKMODE lockmode, ListCell *indexoidscan; int i; + Assert(lockmode != NoLock); + indexoidlist = RelationGetIndexList(relation); *nindexes = list_length(indexoidlist); @@ -3489,11 +3491,8 @@ vac_open_indexes(Relation relation, LOCKMODE lockmode, foreach(indexoidscan, indexoidlist) { Oid indexoid = lfirst_oid(indexoidscan); - Relation ind; - ind = index_open(indexoid); - (*Irel)[i++] = ind; - LockRelation(ind, lockmode); + (*Irel)[i++] = index_open(indexoid, lockmode); } list_free(indexoidlist); @@ -3513,9 +3512,7 @@ vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode) { Relation ind = Irel[nindexes]; - if (lockmode != NoLock) - UnlockRelation(ind, lockmode); - index_close(ind); + index_close(ind, lockmode); } pfree(Irel); } diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index d2727c4a47..2202e7b7e9 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -31,7 +31,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.75 2006/07/14 14:52:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.76 2006/07/31 20:09:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -164,7 +164,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt) vacrelstats->minxid = RecentXmin; /* Open all indexes of the relation */ - vac_open_indexes(onerel, ShareUpdateExclusiveLock, &nindexes, &Irel); + vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel); hasindex = (nindexes > 0); /* Do the vacuuming */ @@ -621,15 +621,6 @@ lazy_vacuum_index(Relation indrel, pg_rusage_init(&ru0); - /* - * Acquire appropriate type of lock on index: must be exclusive if index - * AM isn't concurrent-safe. - */ - if (indrel->rd_am->amconcurrent) - LockRelation(indrel, RowExclusiveLock); - else - LockRelation(indrel, AccessExclusiveLock); - ivinfo.index = indrel; ivinfo.vacuum_full = false; ivinfo.message_level = elevel; @@ -640,14 +631,6 @@ lazy_vacuum_index(Relation indrel, *stats = index_bulk_delete(&ivinfo, *stats, lazy_tid_reaped, (void *) vacrelstats); - /* - * Release lock acquired above. - */ - if (indrel->rd_am->amconcurrent) - UnlockRelation(indrel, RowExclusiveLock); - else - UnlockRelation(indrel, AccessExclusiveLock); - ereport(elevel, (errmsg("scanned index \"%s\" to remove %d row versions", RelationGetRelationName(indrel), @@ -668,15 +651,6 @@ lazy_cleanup_index(Relation indrel, pg_rusage_init(&ru0); - /* - * Acquire appropriate type of lock on index: must be exclusive if index - * AM isn't concurrent-safe. - */ - if (indrel->rd_am->amconcurrent) - LockRelation(indrel, RowExclusiveLock); - else - LockRelation(indrel, AccessExclusiveLock); - ivinfo.index = indrel; ivinfo.vacuum_full = false; ivinfo.message_level = elevel; @@ -684,14 +658,6 @@ lazy_cleanup_index(Relation indrel, stats = index_vacuum_cleanup(&ivinfo, stats); - /* - * Release lock acquired above. - */ - if (indrel->rd_am->amconcurrent) - UnlockRelation(indrel, RowExclusiveLock); - else - UnlockRelation(indrel, AccessExclusiveLock); - if (!stats) return; diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 11f2ae20f5..c879de3bbc 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.137 2006/07/14 14:52:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.138 2006/07/31 20:09:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -795,12 +795,6 @@ ExecCloseScanRelation(Relation scanrel) * * At entry, caller has already opened and locked * resultRelInfo->ri_RelationDesc. - * - * This used to be horribly ugly code, and slow too because it - * did a sequential scan of pg_index. Now we rely on the relcache - * to cache a list of the OIDs of the indices associated with any - * specific relation, and we use the pg_index syscache to get the - * entries we need from pg_index. * ---------------------------------------------------------------- */ void @@ -840,6 +834,7 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo) /* * For each index, open the index relation and save pg_index info. + * We acquire RowExclusiveLock, signifying we will update the index. */ i = 0; foreach(l, indexoidlist) @@ -848,31 +843,7 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo) Relation indexDesc; IndexInfo *ii; - /* - * Open and lock the index relation - * - * If the index AM supports concurrent updates, obtain - * RowExclusiveLock to signify that we are updating the index. This - * locks out only operations that need exclusive access, such as - * relocating the index to a new tablespace. - * - * If the index AM is not safe for concurrent updates, obtain an - * exclusive lock on the index to lock out other updaters as well as - * readers (index_beginscan places AccessShareLock on the index). - * - * If there are multiple not-concurrent-safe indexes, all backends - * must lock the indexes in the same order or we will get deadlocks - * here. This is guaranteed by RelationGetIndexList(), which promises - * to return the index list in OID order. - * - * The locks will be released in ExecCloseIndices. - */ - indexDesc = index_open(indexOid); - - if (indexDesc->rd_am->amconcurrent) - LockRelation(indexDesc, RowExclusiveLock); - else - LockRelation(indexDesc, AccessExclusiveLock); + indexDesc = index_open(indexOid, RowExclusiveLock); /* extract index key information from the index's pg_index info */ ii = BuildIndexInfo(indexDesc); @@ -907,12 +878,7 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo) continue; /* shouldn't happen? */ /* Drop lock acquired by ExecOpenIndices */ - if (indexDescs[i]->rd_am->amconcurrent) - UnlockRelation(indexDescs[i], RowExclusiveLock); - else - UnlockRelation(indexDescs[i], AccessExclusiveLock); - - index_close(indexDescs[i]); + index_close(indexDescs[i], RowExclusiveLock); } /* diff --git a/src/backend/executor/nodeBitmapIndexscan.c b/src/backend/executor/nodeBitmapIndexscan.c index 7555b5a22c..6a0303cddd 100644 --- a/src/backend/executor/nodeBitmapIndexscan.c +++ b/src/backend/executor/nodeBitmapIndexscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeBitmapIndexscan.c,v 1.19 2006/05/30 14:01:58 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeBitmapIndexscan.c,v 1.20 2006/07/31 20:09:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -201,7 +201,7 @@ ExecEndBitmapIndexScan(BitmapIndexScanState *node) * close the index relation */ index_endscan(indexScanDesc); - index_close(indexRelationDesc); + index_close(indexRelationDesc, NoLock); } /* ---------------------------------------------------------------- @@ -258,8 +258,14 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags) /* * Open the index relation. + * + * If the parent table is one of the target relations of the query, then + * InitPlan already opened and write-locked the index, so we can avoid + * taking another lock here. Otherwise we need a normal reader's lock. */ - indexstate->biss_RelationDesc = index_open(node->indexid); + relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid); + indexstate->biss_RelationDesc = index_open(node->indexid, + relistarget ? NoLock : AccessShareLock); /* * Initialize index-specific scan state @@ -303,18 +309,9 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags) /* * Initialize scan descriptor. - * - * Note we acquire no locks here; the index machinery does its own locks - * and unlocks. (We rely on having a lock on the parent table to - * ensure the index won't go away!) Furthermore, if the parent table - * is one of the target relations of the query, then InitPlan already - * opened and write-locked the index, so we can tell the index machinery - * not to bother getting an extra lock. */ - relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid); indexstate->biss_ScanDesc = index_beginscan_multi(indexstate->biss_RelationDesc, - !relistarget, estate->es_snapshot, indexstate->biss_NumScanKeys, indexstate->biss_ScanKeys); diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 02f83667a4..84ee56beb0 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.115 2006/07/14 14:52:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.116 2006/07/31 20:09:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -415,7 +415,7 @@ ExecEndIndexScan(IndexScanState *node) * close the index relation */ index_endscan(indexScanDesc); - index_close(indexRelationDesc); + index_close(indexRelationDesc, NoLock); /* * close the heap relation. @@ -517,8 +517,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) /* * Open the index relation. + * + * If the parent table is one of the target relations of the query, then + * InitPlan already opened and write-locked the index, so we can avoid + * taking another lock here. Otherwise we need a normal reader's lock. */ - indexstate->iss_RelationDesc = index_open(node->indexid); + relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid); + indexstate->iss_RelationDesc = index_open(node->indexid, + relistarget ? NoLock : AccessShareLock); /* * Initialize index-specific scan state @@ -561,18 +567,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) /* * Initialize scan descriptor. - * - * Note we acquire no locks here; the index machinery does its own locks - * and unlocks. (We rely on having a lock on the parent table to - * ensure the index won't go away!) Furthermore, if the parent table - * is one of the target relations of the query, then InitPlan already - * opened and write-locked the index, so we can tell the index machinery - * not to bother getting an extra lock. */ - relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid); indexstate->iss_ScanDesc = index_beginscan(currentRelation, indexstate->iss_RelationDesc, - !relistarget, estate->es_snapshot, indexstate->iss_NumScanKeys, indexstate->iss_ScanKeys); diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index ff453336a1..bafe1b6673 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.121 2006/07/14 14:52:21 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.122 2006/07/31 20:09:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -64,7 +64,7 @@ static List *get_relation_constraints(Oid relationObjectId, RelOptInfo *rel); * widths here, and we may as well cache the results for costsize.c. */ void -get_relation_info(Oid relationObjectId, RelOptInfo *rel) +get_relation_info(PlannerInfo *root, Oid relationObjectId, RelOptInfo *rel) { Index varno = rel->relid; Relation relation; @@ -105,9 +105,23 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel) { List *indexoidlist; ListCell *l; + LOCKMODE lmode; indexoidlist = RelationGetIndexList(relation); + /* + * For each index, we get the same type of lock that the executor will + * need, and do not release it. This saves a couple of trips to the + * shared lock manager while not creating any real loss of + * concurrency, because no schema changes could be happening on the + * index while we hold lock on the parent rel, and neither lock type + * blocks any other kind of index operation. + */ + if (rel->relid == root->parse->resultRelation) + lmode = RowExclusiveLock; + else + lmode = AccessShareLock; + foreach(l, indexoidlist) { Oid indexoid = lfirst_oid(l); @@ -120,13 +134,8 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel) /* * Extract info from the relation descriptor for the index. - * - * Note that we take no lock on the index; we assume our lock on - * the parent table will protect the index's schema information. - * When and if the executor actually uses the index, it will take - * a lock as needed to protect the access to the index contents. */ - indexRelation = index_open(indexoid); + indexRelation = index_open(indexoid, lmode); index = indexRelation->rd_index; info = makeNode(IndexOptInfo); @@ -203,7 +212,7 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel) info->tuples = rel->tuples; } - index_close(indexRelation); + index_close(indexRelation, NoLock); indexinfos = lcons(info, indexinfos); } diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index d20a815461..8d06254a9f 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.79 2006/07/14 14:52:21 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.80 2006/07/31 20:09:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -92,7 +92,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) { case RTE_RELATION: /* Table --- retrieve statistics from the system catalogs */ - get_relation_info(rte->relid, rel); + get_relation_info(root, rte->relid, rel); break; case RTE_SUBQUERY: case RTE_FUNCTION: diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 90d47b09dc..25a19b2b24 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.25 2006/07/14 14:52:22 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.26 2006/07/31 20:09:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -47,7 +47,6 @@ #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/ps_status.h" -#include "utils/relcache.h" #include "utils/syscache.h" @@ -764,7 +763,6 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry, List **vacuum_tables, List **toast_table_ids) { - Relation rel; float4 reltuples; /* pg_class.reltuples */ /* constants from pg_autovacuum or GUC variables */ @@ -799,12 +797,7 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry, if (!PointerIsValid(tabentry)) return; - rel = RelationIdGetRelation(relid); - /* The table was recently dropped? */ - if (!PointerIsValid(rel)) - return; - - reltuples = rel->rd_rel->reltuples; + reltuples = classForm->reltuples; vactuples = tabentry->n_dead_tuples; anltuples = tabentry->n_live_tuples + tabentry->n_dead_tuples - tabentry->last_anl_tuples; @@ -861,7 +854,7 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry, */ elog(DEBUG3, "%s: vac: %.0f (threshold %.0f), anl: %.0f (threshold %.0f)", - RelationGetRelationName(rel), + NameStr(classForm->relname), vactuples, vacthresh, anltuples, anlthresh); /* Determine if this table needs vacuum or analyze. */ @@ -880,7 +873,7 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry, elog(DEBUG2, "autovac: will%s%s %s", (dovacuum ? " VACUUM" : ""), (doanalyze ? " ANALYZE" : ""), - RelationGetRelationName(rel)); + NameStr(classForm->relname)); /* * we must record tables that have a toast table, even if we currently @@ -907,8 +900,6 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry, if (dovacuum) *toast_table_ids = lappend_oid(*toast_table_ids, relid); } - - RelationClose(rel); } /* @@ -966,10 +957,10 @@ autovacuum_do_vac_analyze(List *relids, bool dovacuum, bool doanalyze, * done with the current one, and exiting right after the last one, so we don't * bother to report "<IDLE>" or some such. */ -#define MAX_AUTOVAC_ACTIV_LEN (NAMEDATALEN * 2 + 32) static void autovac_report_activity(VacuumStmt *vacstmt, List *relids) { +#define MAX_AUTOVAC_ACTIV_LEN (NAMEDATALEN * 2 + 32) char activity[MAX_AUTOVAC_ACTIV_LEN]; /* @@ -982,33 +973,32 @@ autovac_report_activity(VacuumStmt *vacstmt, List *relids) /* Report the command and possible options */ if (vacstmt->vacuum) snprintf(activity, MAX_AUTOVAC_ACTIV_LEN, - "VACUUM%s%s%s", - vacstmt->full ? " FULL" : "", - vacstmt->freeze ? " FREEZE" : "", - vacstmt->analyze ? " ANALYZE" : ""); + "VACUUM%s%s%s", + vacstmt->full ? " FULL" : "", + vacstmt->freeze ? " FREEZE" : "", + vacstmt->analyze ? " ANALYZE" : ""); else if (vacstmt->analyze) snprintf(activity, MAX_AUTOVAC_ACTIV_LEN, - "ANALYZE"); + "ANALYZE"); /* Report the qualified name of the first relation, if any */ - if (list_length(relids) > 0) + if (relids) { Oid relid = linitial_oid(relids); - Relation rel; + char *relname = get_rel_name(relid); + char *nspname = get_namespace_name(get_rel_namespace(relid)); - rel = RelationIdGetRelation(relid); - if (rel == NULL) - elog(WARNING, "cache lookup failed for relation %u", relid); - else + /* + * Paranoia is appropriate here in case relation was recently + * dropped --- the lsyscache routines we just invoked will return + * NULL rather than failing. + */ + if (relname && nspname) { - char *nspname = get_namespace_name(RelationGetNamespace(rel)); int len = strlen(activity); snprintf(activity + len, MAX_AUTOVAC_ACTIV_LEN - len, - " %s.%s", nspname, RelationGetRelationName(rel)); - - pfree(nspname); - RelationClose(rel); + " %s.%s", nspname, relname); } } diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c index fd6ae5abc3..582eabdb2a 100644 --- a/src/backend/storage/large_object/inv_api.c +++ b/src/backend/storage/large_object/inv_api.c @@ -17,7 +17,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.118 2006/07/14 14:52:23 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.119 2006/07/31 20:09:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -69,7 +69,7 @@ open_lo_relation(void) if (lo_heap_r == NULL) lo_heap_r = heap_open(LargeObjectRelationId, RowExclusiveLock); if (lo_index_r == NULL) - lo_index_r = index_open(LargeObjectLOidPNIndexId); + lo_index_r = index_open(LargeObjectLOidPNIndexId, RowExclusiveLock); } PG_CATCH(); { @@ -103,7 +103,7 @@ close_lo_relation(bool isCommit) CurrentResourceOwner = TopTransactionResourceOwner; if (lo_index_r) - index_close(lo_index_r); + index_close(lo_index_r, NoLock); if (lo_heap_r) heap_close(lo_heap_r, NoLock); } @@ -314,7 +314,7 @@ inv_getsize(LargeObjectDesc *obj_desc) BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(obj_desc->id)); - sd = index_beginscan(lo_heap_r, lo_index_r, true, + sd = index_beginscan(lo_heap_r, lo_index_r, obj_desc->snapshot, 1, skey); /* @@ -425,7 +425,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes) BTGreaterEqualStrategyNumber, F_INT4GE, Int32GetDatum(pageno)); - sd = index_beginscan(lo_heap_r, lo_index_r, true, + sd = index_beginscan(lo_heap_r, lo_index_r, obj_desc->snapshot, 2, skey); while ((tuple = index_getnext(sd, ForwardScanDirection)) != NULL) @@ -541,7 +541,7 @@ inv_write(LargeObjectDesc *obj_desc, char *buf, int nbytes) BTGreaterEqualStrategyNumber, F_INT4GE, Int32GetDatum(pageno)); - sd = index_beginscan(lo_heap_r, lo_index_r, false /* got lock */, + sd = index_beginscan(lo_heap_r, lo_index_r, obj_desc->snapshot, 2, skey); oldtuple = NULL; diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c index 8e99d4be48..ad7ee3e601 100644 --- a/src/backend/storage/lmgr/lmgr.c +++ b/src/backend/storage/lmgr/lmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.85 2006/07/14 16:59:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.86 2006/07/31 20:09:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,10 +18,13 @@ #include "access/subtrans.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/catalog.h" +#include "catalog/namespace.h" #include "miscadmin.h" #include "storage/lmgr.h" #include "storage/procarray.h" #include "utils/inval.h" +#include "utils/lsyscache.h" /* @@ -44,8 +47,106 @@ RelationInitLockInfo(Relation relation) relation->rd_lockInfo.lockRelId.dbId = MyDatabaseId; } +/* + * SetLocktagRelationOid + * Set up a locktag for a relation, given only relation OID + */ +static inline void +SetLocktagRelationOid(LOCKTAG *tag, Oid relid) +{ + Oid dbid; + + if (IsSharedRelation(relid)) + dbid = InvalidOid; + else + dbid = MyDatabaseId; + + SET_LOCKTAG_RELATION(*tag, dbid, relid); +} + +/* + * LockRelationOid + * + * Lock a relation given only its OID. This should generally be used + * before attempting to open the relation's relcache entry. + */ +void +LockRelationOid(Oid relid, LOCKMODE lockmode) +{ + LOCKTAG tag; + LockAcquireResult res; + + SetLocktagRelationOid(&tag, relid); + + res = LockAcquire(&tag, lockmode, false, false); + + /* + * Now that we have the lock, check for invalidation messages, so that + * we will update or flush any stale relcache entry before we try to use + * it. We can skip this in the not-uncommon case that we already had + * the same type of lock being requested, since then no one else could + * have modified the relcache entry in an undesirable way. (In the + * case where our own xact modifies the rel, the relcache update happens + * via CommandCounterIncrement, not here.) + */ + if (res != LOCKACQUIRE_ALREADY_HELD) + AcceptInvalidationMessages(); +} + +/* + * ConditionalLockRelationOid + * + * As above, but only lock if we can get the lock without blocking. + * Returns TRUE iff the lock was acquired. + * + * NOTE: we do not currently need conditional versions of all the + * LockXXX routines in this file, but they could easily be added if needed. + */ +bool +ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode) +{ + LOCKTAG tag; + LockAcquireResult res; + + SetLocktagRelationOid(&tag, relid); + + res = LockAcquire(&tag, lockmode, false, true); + + if (res == LOCKACQUIRE_NOT_AVAIL) + return false; + + /* + * Now that we have the lock, check for invalidation messages; see + * notes in LockRelationOid. + */ + if (res != LOCKACQUIRE_ALREADY_HELD) + AcceptInvalidationMessages(); + + return true; +} + +/* + * UnlockRelationId + * + * Note: we don't supply UnlockRelationOid since it's normally easy for + * callers to provide the LockRelId info from a relcache entry. + */ +void +UnlockRelationId(LockRelId *relid, LOCKMODE lockmode) +{ + LOCKTAG tag; + + SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId); + + LockRelease(&tag, lockmode, false); +} + /* * LockRelation + * + * This is a convenience routine for acquiring an additional lock on an + * already-open relation. Never try to do "relation_open(foo, NoLock)" + * and then lock with this. */ void LockRelation(Relation relation, LOCKMODE lockmode) @@ -57,31 +158,22 @@ LockRelation(Relation relation, LOCKMODE lockmode) relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.relId); - res = LockAcquire(&tag, relation->rd_istemp, lockmode, false, false); + res = LockAcquire(&tag, lockmode, false, false); /* - * Check to see if the relcache entry has been invalidated while we were - * waiting to lock it. If so, rebuild it, or ereport() trying. Increment - * the refcount to ensure that RelationFlushRelation will rebuild it and - * not just delete it. We can skip this if the lock was already held, - * however. + * Now that we have the lock, check for invalidation messages; see + * notes in LockRelationOid. */ if (res != LOCKACQUIRE_ALREADY_HELD) - { - RelationIncrementReferenceCount(relation); AcceptInvalidationMessages(); - RelationDecrementReferenceCount(relation); - } } /* * ConditionalLockRelation * - * As above, but only lock if we can get the lock without blocking. - * Returns TRUE iff the lock was acquired. - * - * NOTE: we do not currently need conditional versions of all the - * LockXXX routines in this file, but they could easily be added if needed. + * This is a convenience routine for acquiring an additional lock on an + * already-open relation. Never try to do "relation_open(foo, NoLock)" + * and then lock with this. */ bool ConditionalLockRelation(Relation relation, LOCKMODE lockmode) @@ -93,30 +185,26 @@ ConditionalLockRelation(Relation relation, LOCKMODE lockmode) relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.relId); - res = LockAcquire(&tag, relation->rd_istemp, lockmode, false, true); + res = LockAcquire(&tag, lockmode, false, true); if (res == LOCKACQUIRE_NOT_AVAIL) return false; /* - * Check to see if the relcache entry has been invalidated while we were - * waiting to lock it. If so, rebuild it, or ereport() trying. Increment - * the refcount to ensure that RelationFlushRelation will rebuild it and - * not just delete it. We can skip this if the lock was already held, - * however. + * Now that we have the lock, check for invalidation messages; see + * notes in LockRelationOid. */ if (res != LOCKACQUIRE_ALREADY_HELD) - { - RelationIncrementReferenceCount(relation); AcceptInvalidationMessages(); - RelationDecrementReferenceCount(relation); - } return true; } /* * UnlockRelation + * + * This is a convenience routine for unlocking a relation without also + * closing it. */ void UnlockRelation(Relation relation, LOCKMODE lockmode) @@ -131,11 +219,11 @@ UnlockRelation(Relation relation, LOCKMODE lockmode) } /* - * LockRelationForSession + * LockRelationIdForSession * * This routine grabs a session-level lock on the target relation. The * session lock persists across transaction boundaries. It will be removed - * when UnlockRelationForSession() is called, or if an ereport(ERROR) occurs, + * when UnlockRelationIdForSession() is called, or if an ereport(ERROR) occurs, * or if the backend exits. * * Note that one should also grab a transaction-level lock on the rel @@ -143,20 +231,20 @@ UnlockRelation(Relation relation, LOCKMODE lockmode) * relcache entry is up to date. */ void -LockRelationForSession(LockRelId *relid, bool istemprel, LOCKMODE lockmode) +LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode) { LOCKTAG tag; SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId); - (void) LockAcquire(&tag, istemprel, lockmode, true, false); + (void) LockAcquire(&tag, lockmode, true, false); } /* - * UnlockRelationForSession + * UnlockRelationIdForSession */ void -UnlockRelationForSession(LockRelId *relid, LOCKMODE lockmode) +UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode) { LOCKTAG tag; @@ -184,7 +272,7 @@ LockRelationForExtension(Relation relation, LOCKMODE lockmode) relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.relId); - (void) LockAcquire(&tag, relation->rd_istemp, lockmode, false, false); + (void) LockAcquire(&tag, lockmode, false, false); } /* @@ -218,7 +306,7 @@ LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode) relation->rd_lockInfo.lockRelId.relId, blkno); - (void) LockAcquire(&tag, relation->rd_istemp, lockmode, false, false); + (void) LockAcquire(&tag, lockmode, false, false); } /* @@ -237,8 +325,7 @@ ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode) relation->rd_lockInfo.lockRelId.relId, blkno); - return (LockAcquire(&tag, relation->rd_istemp, - lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL); + return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL); } /* @@ -275,7 +362,7 @@ LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode) ItemPointerGetBlockNumber(tid), ItemPointerGetOffsetNumber(tid)); - (void) LockAcquire(&tag, relation->rd_istemp, lockmode, false, false); + (void) LockAcquire(&tag, lockmode, false, false); } /* @@ -295,8 +382,7 @@ ConditionalLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode) ItemPointerGetBlockNumber(tid), ItemPointerGetOffsetNumber(tid)); - return (LockAcquire(&tag, relation->rd_istemp, - lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL); + return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL); } /* @@ -330,7 +416,7 @@ XactLockTableInsert(TransactionId xid) SET_LOCKTAG_TRANSACTION(tag, xid); - (void) LockAcquire(&tag, false, ExclusiveLock, false, false); + (void) LockAcquire(&tag, ExclusiveLock, false, false); } /* @@ -375,7 +461,7 @@ XactLockTableWait(TransactionId xid) SET_LOCKTAG_TRANSACTION(tag, xid); - (void) LockAcquire(&tag, false, ShareLock, false, false); + (void) LockAcquire(&tag, ShareLock, false, false); LockRelease(&tag, ShareLock, false); @@ -403,8 +489,7 @@ ConditionalXactLockTableWait(TransactionId xid) SET_LOCKTAG_TRANSACTION(tag, xid); - if (LockAcquire(&tag, false, - ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL) + if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL) return false; LockRelease(&tag, ShareLock, false); @@ -423,9 +508,7 @@ ConditionalXactLockTableWait(TransactionId xid) * Obtain a lock on a general object of the current database. Don't use * this for shared objects (such as tablespaces). It's unwise to apply it * to relations, also, since a lock taken this way will NOT conflict with - * LockRelation, and also may be wrongly marked if the relation is temp. - * (If we ever invent temp objects that aren't tables, we'll want to extend - * the API of this routine to include an isTempObject flag.) + * locks taken via LockRelation and friends. */ void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid, @@ -439,7 +522,7 @@ LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid, objid, objsubid); - (void) LockAcquire(&tag, false, lockmode, false, false); + (void) LockAcquire(&tag, lockmode, false, false); } /* @@ -477,7 +560,7 @@ LockSharedObject(Oid classid, Oid objid, uint16 objsubid, objid, objsubid); - (void) LockAcquire(&tag, false, lockmode, false, false); + (void) LockAcquire(&tag, lockmode, false, false); /* Make sure syscaches are up-to-date with any changes we waited for */ AcceptInvalidationMessages(); @@ -500,3 +583,39 @@ UnlockSharedObject(Oid classid, Oid objid, uint16 objsubid, LockRelease(&tag, lockmode, false); } + + +/* + * LockTagIsTemp + * Determine whether a locktag is for a lock on a temporary object + * + * We need this because 2PC cannot deal with temp objects + */ +bool +LockTagIsTemp(const LOCKTAG *tag) +{ + switch (tag->locktag_type) + { + case LOCKTAG_RELATION: + case LOCKTAG_RELATION_EXTEND: + case LOCKTAG_PAGE: + case LOCKTAG_TUPLE: + /* check for lock on a temp relation */ + /* field1 is dboid, field2 is reloid for all of these */ + if ((Oid) tag->locktag_field1 == InvalidOid) + return false; /* shared, so not temp */ + if (isTempNamespace(get_rel_namespace((Oid) tag->locktag_field2))) + return true; + break; + case LOCKTAG_TRANSACTION: + /* there are no temp transactions */ + break; + case LOCKTAG_OBJECT: + /* there are currently no non-table temp objects */ + break; + case LOCKTAG_USERLOCK: + /* assume these aren't temp */ + break; + } + return false; /* default case */ +} diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index a0bc2869c0..10049d593a 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.169 2006/07/24 16:32:45 petere Exp $ + * $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.170 2006/07/31 20:09:05 tgl Exp $ * * NOTES * A lock table is a shared memory hash table. When @@ -36,6 +36,7 @@ #include "access/twophase.h" #include "access/twophase_rmgr.h" #include "miscadmin.h" +#include "storage/lmgr.h" #include "utils/memutils.h" #include "utils/ps_status.h" #include "utils/resowner.h" @@ -449,8 +450,6 @@ ProcLockHashCode(const PROCLOCKTAG *proclocktag, uint32 hashcode) * * Inputs: * locktag: unique identifier for the lockable object - * isTempObject: is the lockable object a temporary object? (Under 2PC, - * such locks cannot be persisted) * lockmode: lock mode to acquire * sessionLock: if true, acquire lock for session not current transaction * dontWait: if true, don't wait to acquire lock @@ -471,7 +470,6 @@ ProcLockHashCode(const PROCLOCKTAG *proclocktag, uint32 hashcode) */ LockAcquireResult LockAcquire(const LOCKTAG *locktag, - bool isTempObject, LOCKMODE lockmode, bool sessionLock, bool dontWait) @@ -528,7 +526,6 @@ LockAcquire(const LOCKTAG *locktag, { locallock->lock = NULL; locallock->proclock = NULL; - locallock->isTempObject = isTempObject; locallock->hashcode = LockTagHashCode(&(localtag.lock)); locallock->nLocks = 0; locallock->numLockOwners = 0; @@ -540,8 +537,6 @@ LockAcquire(const LOCKTAG *locktag, } else { - Assert(locallock->isTempObject == isTempObject); - /* Make sure there will be room to remember the lock */ if (locallock->numLockOwners >= locallock->maxLockOwners) { @@ -1733,7 +1728,7 @@ AtPrepare_Locks(void) } /* Can't handle it if the lock is on a temporary object */ - if (locallock->isTempObject) + if (LockTagIsTemp(&locallock->tag.lock)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot PREPARE a transaction that has operated on temporary tables"))); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index f78db61968..20b83e196a 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.131 2006/07/14 14:52:25 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.132 2006/07/31 20:09:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -904,12 +904,7 @@ CatalogCacheInitializeCache(CatCache *cache) CatalogCacheInitializeCache_DEBUG1; - /* - * Open the relation without locking --- we only need the tupdesc, which - * we assume will never change ... - */ - relation = heap_open(cache->cc_reloid, NoLock); - Assert(RelationIsValid(relation)); + relation = heap_open(cache->cc_reloid, AccessShareLock); /* * switch to the cache context so our allocations do not vanish at the end @@ -936,7 +931,7 @@ CatalogCacheInitializeCache(CatCache *cache) */ MemoryContextSwitchTo(oldcxt); - heap_close(relation, NoLock); + heap_close(relation, AccessShareLock); CACHE3_elog(DEBUG2, "CatalogCacheInitializeCache: %s, %d keys", cache->cc_relname, cache->cc_nkeys); @@ -1012,8 +1007,8 @@ InitCatCachePhase2(CatCache *cache) { Relation idesc; - idesc = index_open(cache->cc_indexoid); - index_close(idesc); + idesc = index_open(cache->cc_indexoid, AccessShareLock); + index_close(idesc, AccessShareLock); } } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 0fe1f29b25..08697d5036 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.246 2006/07/14 14:52:25 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.247 2006/07/31 20:09:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,6 @@ * RelationCacheInitialize - initialize relcache (to empty) * RelationCacheInitializePhase2 - finish initializing relcache * RelationIdGetRelation - get a reldesc by relation id - * RelationIdCacheGetRelation - get a cached reldesc by relid * RelationClose - close an open relation * * NOTES @@ -34,6 +33,7 @@ #include "access/heapam.h" #include "access/reloptions.h" #include "access/xact.h" +#include "catalog/catalog.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_amop.h" @@ -763,6 +763,10 @@ equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2) * recycling the given old relation object. The latter case * supports rebuilding a relcache entry without invalidating * pointers to it. + * + * Returns NULL if no pg_class row could be found for the given relid + * (suggesting we are trying to access a just-deleted relation). + * Any other error is reported via elog. * -------------------------------- */ static Relation @@ -1387,41 +1391,18 @@ formrdesc(const char *relationName, Oid relationReltype, * ---------------------------------------------------------------- */ -/* - * RelationIdCacheGetRelation - * - * Lookup an existing reldesc by OID. - * - * Only try to get the reldesc by looking in the cache, - * do not go to the disk if it's not present. - * - * NB: relation ref count is incremented if successful. - * Caller should eventually decrement count. (Usually, - * that happens by calling RelationClose().) - */ -Relation -RelationIdCacheGetRelation(Oid relationId) -{ - Relation rd; - - RelationIdCacheLookup(relationId, rd); - - if (RelationIsValid(rd)) - { - RelationIncrementReferenceCount(rd); - /* revalidate nailed index if necessary */ - if (!rd->rd_isvalid) - RelationReloadClassinfo(rd); - } - - return rd; -} - /* * RelationIdGetRelation * * Lookup a reldesc by OID; make one if not already in cache. * + * Returns NULL if no pg_class row could be found for the given relid + * (suggesting we are trying to access a just-deleted relation). + * Any other error is reported via elog. + * + * NB: caller should already have at least AccessShareLock on the + * relation ID, else there are nasty race conditions. + * * NB: relation ref count is incremented, or set to 1 if new entry. * Caller should eventually decrement count. (Usually, * that happens by calling RelationClose().) @@ -1432,11 +1413,18 @@ RelationIdGetRelation(Oid relationId) Relation rd; /* - * first try and get a reldesc from the cache + * first try to find reldesc in the cache */ - rd = RelationIdCacheGetRelation(relationId); + RelationIdCacheLookup(relationId, rd); + if (RelationIsValid(rd)) + { + RelationIncrementReferenceCount(rd); + /* revalidate nailed index if necessary */ + if (!rd->rd_isvalid) + RelationReloadClassinfo(rd); return rd; + } /* * no reldesc in the cache, so have RelationBuildDesc() build one and add @@ -2133,6 +2121,16 @@ RelationBuildLocalRelation(const char *relname, break; } + /* + * check that hardwired list of shared rels matches what's in the + * bootstrap .bki file. If you get a failure here during initdb, + * you probably need to fix IsSharedRelation() to match whatever + * you've done to the set of shared relations. + */ + if (shared_relation != IsSharedRelation(relid)) + elog(ERROR, "shared_relation flag for \"%s\" does not match IsSharedRelation(%u)", + relname, relid); + /* * switch to the cache context to create the relcache entry. */ diff --git a/src/include/access/genam.h b/src/include/access/genam.h index 9cd9162c26..ddad174527 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.64 2006/07/13 17:47:01 momjian Exp $ + * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.65 2006/07/31 20:09:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "access/relscan.h" #include "access/sdir.h" #include "nodes/primnodes.h" +#include "storage/lock.h" /* * Struct for statistics returned by ambuild @@ -84,9 +85,9 @@ typedef SysScanDescData *SysScanDesc; /* * generalized index_ interface routines (in indexam.c) */ -extern Relation index_open(Oid relationId); -extern Relation index_openrv(const RangeVar *relation); -extern void index_close(Relation relation); +extern Relation index_open(Oid relationId, LOCKMODE lockmode); +extern void index_close(Relation relation, LOCKMODE lockmode); + extern bool index_insert(Relation indexRelation, Datum *values, bool *isnull, ItemPointer heap_t_ctid, @@ -95,11 +96,9 @@ extern bool index_insert(Relation indexRelation, extern IndexScanDesc index_beginscan(Relation heapRelation, Relation indexRelation, - bool need_index_lock, Snapshot snapshot, int nkeys, ScanKey key); extern IndexScanDesc index_beginscan_multi(Relation indexRelation, - bool need_index_lock, Snapshot snapshot, int nkeys, ScanKey key); extern void index_rescan(IndexScanDesc scan, ScanKey key); diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h index cbe7b0c244..d32ab6d524 100644 --- a/src/include/access/relscan.h +++ b/src/include/access/relscan.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/relscan.h,v 1.48 2006/07/13 18:01:01 momjian Exp $ + * $PostgreSQL: pgsql/src/include/access/relscan.h,v 1.49 2006/07/31 20:09:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -62,7 +62,6 @@ typedef struct IndexScanDescData int numberOfKeys; /* number of scan keys */ ScanKey keyData; /* array of scan key descriptors */ bool is_multiscan; /* TRUE = using amgetmulti */ - bool have_lock; /* TRUE = holding AccessShareLock for scan */ /* signaling to index AM about killing index tuples */ bool kill_prior_tuple; /* last-returned tuple is dead */ diff --git a/src/include/catalog/catalog.h b/src/include/catalog/catalog.h index 6136a33f24..512ea9adbf 100644 --- a/src/include/catalog/catalog.h +++ b/src/include/catalog/catalog.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catalog.h,v 1.35 2006/03/05 15:58:54 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catalog.h,v 1.36 2006/07/31 20:09:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -31,6 +31,8 @@ extern bool IsToastNamespace(Oid namespaceId); extern bool IsReservedName(const char *name); +extern bool IsSharedRelation(Oid relationId); + extern Oid GetNewOid(Relation relation); extern Oid GetNewOidWithIndex(Relation relation, Relation indexrel); extern Oid GetNewRelFileNode(Oid reltablespace, bool relisshared, diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 052d2a19cf..23b0ac9ce4 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.344 2006/07/31 01:16:37 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.345 2006/07/31 20:09:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200607301 +#define CATALOG_VERSION_NO 200607311 #endif diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h index 048e3e97f7..7b155bf828 100644 --- a/src/include/catalog/pg_am.h +++ b/src/include/catalog/pg_am.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.45 2006/07/03 22:45:40 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.46 2006/07/31 20:09:05 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -51,7 +51,6 @@ CATALOG(pg_am,2601) bool amoptionalkey; /* can query omit key for the first column? */ bool amindexnulls; /* does AM support NULL index entries? */ bool amstorage; /* can storage type differ from column type? */ - bool amconcurrent; /* does AM support concurrent updates? */ bool amclusterable; /* does AM support cluster command? */ regproc aminsert; /* "insert this tuple" function */ regproc ambeginscan; /* "start new scan" function */ @@ -79,7 +78,7 @@ typedef FormData_pg_am *Form_pg_am; * compiler constants for pg_am * ---------------- */ -#define Natts_pg_am 24 +#define Natts_pg_am 23 #define Anum_pg_am_amname 1 #define Anum_pg_am_amstrategies 2 #define Anum_pg_am_amsupport 3 @@ -89,37 +88,36 @@ typedef FormData_pg_am *Form_pg_am; #define Anum_pg_am_amoptionalkey 7 #define Anum_pg_am_amindexnulls 8 #define Anum_pg_am_amstorage 9 -#define Anum_pg_am_amconcurrent 10 -#define Anum_pg_am_amclusterable 11 -#define Anum_pg_am_aminsert 12 -#define Anum_pg_am_ambeginscan 13 -#define Anum_pg_am_amgettuple 14 -#define Anum_pg_am_amgetmulti 15 -#define Anum_pg_am_amrescan 16 -#define Anum_pg_am_amendscan 17 -#define Anum_pg_am_ammarkpos 18 -#define Anum_pg_am_amrestrpos 19 -#define Anum_pg_am_ambuild 20 -#define Anum_pg_am_ambulkdelete 21 -#define Anum_pg_am_amvacuumcleanup 22 -#define Anum_pg_am_amcostestimate 23 -#define Anum_pg_am_amoptions 24 +#define Anum_pg_am_amclusterable 10 +#define Anum_pg_am_aminsert 11 +#define Anum_pg_am_ambeginscan 12 +#define Anum_pg_am_amgettuple 13 +#define Anum_pg_am_amgetmulti 14 +#define Anum_pg_am_amrescan 15 +#define Anum_pg_am_amendscan 16 +#define Anum_pg_am_ammarkpos 17 +#define Anum_pg_am_amrestrpos 18 +#define Anum_pg_am_ambuild 19 +#define Anum_pg_am_ambulkdelete 20 +#define Anum_pg_am_amvacuumcleanup 21 +#define Anum_pg_am_amcostestimate 22 +#define Anum_pg_am_amoptions 23 /* ---------------- * initial contents of pg_am * ---------------- */ -DATA(insert OID = 403 ( btree 5 1 1 t t t t f t t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions )); +DATA(insert OID = 403 ( btree 5 1 1 t t t t f t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions )); DESCR("b-tree index access method"); #define BTREE_AM_OID 403 -DATA(insert OID = 405 ( hash 1 1 0 f f f f f t f hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions )); +DATA(insert OID = 405 ( hash 1 1 0 f f f f f f hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions )); DESCR("hash index access method"); #define HASH_AM_OID 405 -DATA(insert OID = 783 ( gist 100 7 0 f t t t t t t gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions )); +DATA(insert OID = 783 ( gist 100 7 0 f t t t t t gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions )); DESCR("GiST index access method"); #define GIST_AM_OID 783 -DATA(insert OID = 2742 ( gin 100 4 0 f f f f t t f gininsert ginbeginscan gingettuple gingetmulti ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions )); +DATA(insert OID = 2742 ( gin 100 4 0 f f f f t f gininsert ginbeginscan gingettuple gingetmulti ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions )); DESCR("GIN index access method"); #define GIN_AM_OID 2742 diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h index c0e86cb80e..322ae97741 100644 --- a/src/include/optimizer/plancat.h +++ b/src/include/optimizer/plancat.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/plancat.h,v 1.39 2006/03/05 15:58:57 momjian Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/plancat.h,v 1.40 2006/07/31 20:09:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,7 @@ #include "nodes/relation.h" -extern void get_relation_info(Oid relationObjectId, RelOptInfo *rel); +extern void get_relation_info(PlannerInfo *root, Oid relationObjectId, RelOptInfo *rel); extern bool relation_excluded_by_constraints(RelOptInfo *rel, RangeTblEntry *rte); diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h index 1eaede10a0..d0f9ba2b9c 100644 --- a/src/include/storage/lmgr.h +++ b/src/include/storage/lmgr.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.54 2006/03/05 15:58:59 momjian Exp $ + * $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.55 2006/07/31 20:09:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,13 +21,16 @@ extern void RelationInitLockInfo(Relation relation); /* Lock a relation */ +extern void LockRelationOid(Oid relid, LOCKMODE lockmode); +extern bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode); +extern void UnlockRelationId(LockRelId *relid, LOCKMODE lockmode); + extern void LockRelation(Relation relation, LOCKMODE lockmode); extern bool ConditionalLockRelation(Relation relation, LOCKMODE lockmode); extern void UnlockRelation(Relation relation, LOCKMODE lockmode); -extern void LockRelationForSession(LockRelId *relid, bool istemprel, - LOCKMODE lockmode); -extern void UnlockRelationForSession(LockRelId *relid, LOCKMODE lockmode); +extern void LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode); +extern void UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode); /* Lock a relation for extension */ extern void LockRelationForExtension(Relation relation, LOCKMODE lockmode); @@ -62,4 +65,7 @@ extern void LockSharedObject(Oid classid, Oid objid, uint16 objsubid, extern void UnlockSharedObject(Oid classid, Oid objid, uint16 objsubid, LOCKMODE lockmode); +/* Knowledge about which locktags describe temp objects */ +extern bool LockTagIsTemp(const LOCKTAG *tag); + #endif /* LMGR_H */ diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h index 7a2651cd28..208b4a93cc 100644 --- a/src/include/storage/lock.h +++ b/src/include/storage/lock.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.96 2006/07/23 23:08:46 tgl Exp $ + * $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.97 2006/07/31 20:09:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -349,7 +349,6 @@ typedef struct LOCALLOCK /* data */ LOCK *lock; /* associated LOCK object in shared mem */ PROCLOCK *proclock; /* associated PROCLOCK object in shmem */ - bool isTempObject; /* true if lock is on a temporary object */ uint32 hashcode; /* copy of LOCKTAG's hash value */ int nLocks; /* total number of times lock is held */ int numLockOwners; /* # of relevant ResourceOwners */ @@ -405,7 +404,6 @@ extern void InitLocks(void); extern LockMethod GetLocksMethodTable(const LOCK *lock); extern uint32 LockTagHashCode(const LOCKTAG *locktag); extern LockAcquireResult LockAcquire(const LOCKTAG *locktag, - bool isTempObject, LOCKMODE lockmode, bool sessionLock, bool dontWait); diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index b30387a7ef..aa2b1608c1 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.54 2006/05/04 18:51:36 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.55 2006/07/31 20:09:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,13 +17,9 @@ #include "utils/rel.h" /* - * relation lookup routines + * Routines to open (lookup) and close a relcache entry */ extern Relation RelationIdGetRelation(Oid relationId); - -/* finds an existing cache entry, but won't make a new one */ -extern Relation RelationIdCacheGetRelation(Oid relationId); - extern void RelationClose(Relation relation); /*