diff options
author | Simon Riggs <simon@2ndQuadrant.com> | 2014-04-06 11:13:43 -0400 |
---|---|---|
committer | Simon Riggs <simon@2ndQuadrant.com> | 2014-04-06 11:13:43 -0400 |
commit | e5550d5fec66aa74caad1f79b79826ec64898688 (patch) | |
tree | 046444c974bf3aa9833545c0b9bbc183c37dbfa1 /src/backend/utils | |
parent | 80a5cf643adb496abe577a1ca6dc0c476d849c19 (diff) | |
download | postgresql-e5550d5fec66aa74caad1f79b79826ec64898688.tar.gz postgresql-e5550d5fec66aa74caad1f79b79826ec64898688.zip |
Reduce lock levels of some ALTER TABLE cmds
VALIDATE CONSTRAINT
CLUSTER ON
SET WITHOUT CLUSTER
ALTER COLUMN SET STATISTICS
ALTER COLUMN SET ()
ALTER COLUMN RESET ()
All other sub-commands use AccessExclusiveLock
Simon Riggs and Noah Misch
Reviews by Robert Haas and Andres Freund
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 33 | ||||
-rw-r--r-- | src/backend/utils/cache/relcache.c | 74 |
2 files changed, 98 insertions, 9 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index b1bac866aa1..ea7b8c59429 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -54,6 +54,7 @@ #include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/rel.h" +#include "utils/snapmgr.h" #include "utils/syscache.h" #include "utils/tqual.h" #include "utils/typcache.h" @@ -1284,6 +1285,9 @@ pg_get_constraintdef_string(Oid constraintId) return pg_get_constraintdef_worker(constraintId, true, 0); } +/* + * As of 9.4, we now use an MVCC snapshot for this. + */ static char * pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags) @@ -1291,10 +1295,34 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, HeapTuple tup; Form_pg_constraint conForm; StringInfoData buf; + SysScanDesc scandesc; + ScanKeyData scankey[1]; + Snapshot snapshot = RegisterSnapshot(GetTransactionSnapshot()); + Relation relation = heap_open(ConstraintRelationId, AccessShareLock); + + ScanKeyInit(&scankey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(constraintId)); + + scandesc = systable_beginscan(relation, + ConstraintOidIndexId, + true, + snapshot, + 1, + scankey); + + /* + * We later use the tuple with SysCacheGetAttr() as if we + * had obtained it via SearchSysCache, which works fine. + */ + tup = systable_getnext(scandesc); + + UnregisterSnapshot(snapshot); - tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintId)); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for constraint %u", constraintId); + conForm = (Form_pg_constraint) GETSTRUCT(tup); initStringInfo(&buf); @@ -1575,7 +1603,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, appendStringInfoString(&buf, " NOT VALID"); /* Cleanup */ - ReleaseSysCache(tup); + systable_endscan(scandesc); + heap_close(relation, AccessShareLock); return buf.data; } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 32313244adb..c8cea028d4e 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -162,6 +162,14 @@ static bool eoxact_list_overflowed = false; eoxact_list_overflowed = true; \ } while (0) +/* + * EOXactTupleDescArray stores TupleDescs that (might) need AtEOXact + * cleanup work. The array expands as needed; there is no hashtable because + * we don't need to access individual items except at EOXact. + */ +static TupleDesc *EOXactTupleDescArray; +static int NextEOXactTupleDescNum = 0; +static int EOXactTupleDescArrayLen = 0; /* * macros to manipulate the lookup hashtables @@ -220,11 +228,12 @@ static HTAB *OpClassCache = NULL; /* non-export function prototypes */ -static void RelationDestroyRelation(Relation relation); +static void RelationDestroyRelation(Relation relation, bool remember_tupdesc); static void RelationClearRelation(Relation relation, bool rebuild); static void RelationReloadIndexInfo(Relation relation); static void RelationFlushRelation(Relation relation); +static void RememberToFreeTupleDescAtEOX(TupleDesc td); static void AtEOXact_cleanup(Relation relation, bool isCommit); static void AtEOSubXact_cleanup(Relation relation, bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid); @@ -1858,7 +1867,7 @@ RelationReloadIndexInfo(Relation relation) * Caller must already have unhooked the entry from the hash table. */ static void -RelationDestroyRelation(Relation relation) +RelationDestroyRelation(Relation relation, bool remember_tupdesc) { Assert(RelationHasReferenceCountZero(relation)); @@ -1878,7 +1887,20 @@ RelationDestroyRelation(Relation relation) /* can't use DecrTupleDescRefCount here */ Assert(relation->rd_att->tdrefcount > 0); if (--relation->rd_att->tdrefcount == 0) - FreeTupleDesc(relation->rd_att); + { + /* + * If we Rebuilt a relcache entry during a transaction then its + * possible we did that because the TupDesc changed as the result + * of an ALTER TABLE that ran at less than AccessExclusiveLock. + * It's possible someone copied that TupDesc, in which case the + * copy would point to free'd memory. So if we rebuild an entry + * we keep the TupDesc around until end of transaction, to be safe. + */ + if (remember_tupdesc) + RememberToFreeTupleDescAtEOX(relation->rd_att); + else + FreeTupleDesc(relation->rd_att); + } list_free(relation->rd_indexlist); bms_free(relation->rd_indexattr); FreeTriggerDesc(relation->trigdesc); @@ -1992,7 +2014,7 @@ RelationClearRelation(Relation relation, bool rebuild) RelationCacheDelete(relation); /* And release storage */ - RelationDestroyRelation(relation); + RelationDestroyRelation(relation, false); } else if (!IsTransactionState()) { @@ -2059,7 +2081,7 @@ RelationClearRelation(Relation relation, bool rebuild) { /* Should only get here if relation was deleted */ RelationCacheDelete(relation); - RelationDestroyRelation(relation); + RelationDestroyRelation(relation, false); elog(ERROR, "relation %u deleted while still in use", save_relid); } @@ -2121,7 +2143,7 @@ RelationClearRelation(Relation relation, bool rebuild) #undef SWAPFIELD /* And now we can throw away the temporary entry */ - RelationDestroyRelation(newrel); + RelationDestroyRelation(newrel, !keep_tupdesc); } } @@ -2359,6 +2381,33 @@ RelationCloseSmgrByOid(Oid relationId) RelationCloseSmgr(relation); } +void +RememberToFreeTupleDescAtEOX(TupleDesc td) +{ + if (EOXactTupleDescArray == NULL) + { + MemoryContext oldcxt; + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + + EOXactTupleDescArray = (TupleDesc *) palloc(16 * sizeof(TupleDesc)); + EOXactTupleDescArrayLen = 16; + NextEOXactTupleDescNum = 0; + MemoryContextSwitchTo(oldcxt); + } + else if (NextEOXactTupleDescNum >= EOXactTupleDescArrayLen) + { + int32 newlen = EOXactTupleDescArrayLen * 2; + + Assert(EOXactTupleDescArrayLen > 0); + + EOXactTupleDescArray = (TupleDesc *) repalloc(EOXactTupleDescArray, + newlen * sizeof(TupleDesc)); + EOXactTupleDescArrayLen = newlen; + } + + EOXactTupleDescArray[NextEOXactTupleDescNum++] = td; +} + /* * AtEOXact_RelationCache * @@ -2414,9 +2463,20 @@ AtEOXact_RelationCache(bool isCommit) } } - /* Now we're out of the transaction and can clear the list */ + if (EOXactTupleDescArrayLen > 0) + { + Assert(EOXactTupleDescArray != NULL); + for (i = 0; i < NextEOXactTupleDescNum; i++) + FreeTupleDesc(EOXactTupleDescArray[i]); + pfree(EOXactTupleDescArray); + EOXactTupleDescArray = NULL; + } + + /* Now we're out of the transaction and can clear the lists */ eoxact_list_len = 0; eoxact_list_overflowed = false; + NextEOXactTupleDescNum = 0; + EOXactTupleDescArrayLen = 0; } /* |