From dc75e3255803617d21c55d77dc218904bd729d81 Mon Sep 17 00:00:00 2001 From: Bertrand Drouvot Date: Fri, 29 Mar 2024 15:43:26 +0000 Subject: [PATCH v10] Avoid orphaned objects dependencies It's currently possible to create orphaned objects dependencies, for example: Scenario 1: session 1: begin; drop schema schem; session 2: create a function in the schema schem session 1: commit; With the above, the function created in session 2 would be linked to a non existing schema. Scenario 2: session 1: begin; create a function in the schema schem session 2: drop schema schem; session 1: commit; With the above, the function created in session 1 would be linked to a non existing schema. To avoid those scenarios, a new lock (that conflicts with a lock taken by DROP) has been put in place before the dependencies are being recorded. With this in place, the drop schema in scenario 2 would be locked. Also, after the new lock attempt, the patch checks that the object still exists: with this in place session 2 in scenario 1 would be locked and would report an error once session 1 committs (that would not be the case should session 1 abort the transaction). If the object is dropped before the new lock attempt is triggered then the patch would also report an error (but with less details). The patch adds a few tests for some dependency cases (that would currently produce orphaned objects): - schema and function (as the above scenarios) - alter a dependency (function and schema) - function and arg type - function and return type - function and function - domain and domain - table and type - server and foreign data wrapper --- src/backend/catalog/aclchk.c | 1 + src/backend/catalog/dependency.c | 109 ++++++++++++++- src/backend/catalog/heap.c | 8 ++ src/backend/catalog/index.c | 16 +++ src/backend/catalog/objectaddress.c | 57 ++++++++ src/backend/catalog/pg_aggregate.c | 9 ++ src/backend/catalog/pg_attrdef.c | 1 + src/backend/catalog/pg_cast.c | 5 + src/backend/catalog/pg_collation.c | 1 + src/backend/catalog/pg_constraint.c | 11 ++ src/backend/catalog/pg_conversion.c | 2 + src/backend/catalog/pg_depend.c | 27 +++- src/backend/catalog/pg_operator.c | 19 +++ src/backend/catalog/pg_proc.c | 17 ++- src/backend/catalog/pg_publication.c | 5 + src/backend/catalog/pg_range.c | 6 + src/backend/catalog/pg_type.c | 33 +++++ src/backend/catalog/toasting.c | 1 + src/backend/commands/alter.c | 4 + src/backend/commands/amcmds.c | 1 + src/backend/commands/cluster.c | 5 + src/backend/commands/event_trigger.c | 1 + src/backend/commands/extension.c | 5 + src/backend/commands/foreigncmds.c | 7 + src/backend/commands/functioncmds.c | 6 + src/backend/commands/indexcmds.c | 2 + src/backend/commands/opclasscmds.c | 18 +++ src/backend/commands/operatorcmds.c | 30 ++++ src/backend/commands/policy.c | 2 + src/backend/commands/proclang.c | 3 + src/backend/commands/sequence.c | 1 + src/backend/commands/statscmds.c | 3 + src/backend/commands/tablecmds.c | 32 +++-- src/backend/commands/trigger.c | 18 ++- src/backend/commands/tsearchcmds.c | 81 +++++++---- src/backend/commands/typecmds.c | 84 ++++++++++++ src/backend/rewrite/rewriteDefine.c | 1 + src/backend/utils/errcodes.txt | 1 + src/include/catalog/dependency.h | 7 + src/include/catalog/objectaddress.h | 1 + .../expected/test_dependencies_locks.out | 129 ++++++++++++++++++ src/test/isolation/isolation_schedule | 1 + .../specs/test_dependencies_locks.spec | 89 ++++++++++++ 43 files changed, 813 insertions(+), 47 deletions(-) 31.4% src/backend/catalog/ 36.4% src/backend/commands/ 18.5% src/test/isolation/expected/ 11.5% src/test/isolation/specs/ diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 143876b77f..5a614f25ff 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -1413,6 +1413,7 @@ SetDefaultACL(InternalDefaultACL *iacls) referenced.objectId = iacls->nspid; referenced.objectSubId = 0; + LockNotPinnedObject(NamespaceRelationId, iacls->nspid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); } } diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 0489cbabcb..abf8b92a6d 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -1519,6 +1519,84 @@ AcquireDeletionLock(const ObjectAddress *object, int flags) } } +/* + * LockNotPinnedObjectById + * + * Lock the object that we are about to record a dependency on. + * After it's locked, verify that it hasn't been dropped while we + * weren't looking. If the object has been dropped, this function + * does not return! + */ +void +LockNotPinnedObjectById(const ObjectAddress *object) +{ + char *object_description; + + if (isObjectPinned(object)) + return; + + /* + * Those don't rely on LockDatabaseObject() when being dropped (see + * AcquireDeletionLock()). Also it looks like they can not produce + * orphaned dependent objects when being dropped. + */ + if (object->classId == RelationRelationId || object->classId == AuthMemRelationId) + return; + + object_description = getObjectDescription(object, true); + + /* assume we should lock the whole object not a sub-object */ + LockDatabaseObject(object->classId, object->objectId, 0, AccessShareLock); + + /* check if object still exists */ + if (!ObjectByIdExist(object)) + { + if (object_description) + ereport(ERROR, + (errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST), + errmsg("%s does not exist", object_description))); + else + ereport(ERROR, + (errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST), + errmsg("a dependent object does not exist"))); + } + + if (object_description) + pfree(object_description); + + return; +} + +void +LockNotPinnedObjectsById(const ObjectAddress *object, int nobject) +{ + int i; + + if (nobject < 0) + return; + + for (i = 0; i < nobject; i++, object++) + LockNotPinnedObjectById(object); + + return; +} + + +/* + * LockNotPinnedObject + * + * Lock the object that we are about to record a dependency on. + */ +void +LockNotPinnedObject(Oid classid, Oid objid) +{ + ObjectAddress object; + + ObjectAddressSet(object, classid, objid); + + LockNotPinnedObjectById(&object); +} + /* * ReleaseDeletionLock - release an object deletion lock * @@ -1564,13 +1642,8 @@ recordDependencyOnExpr(const ObjectAddress *depender, /* Scan the expression tree for referenceable objects */ find_expr_references_walker(expr, &context); - /* Remove any duplicates */ - eliminate_duplicate_dependencies(context.addrs); - - /* And record 'em */ - recordMultipleDependencies(depender, - context.addrs->refs, context.addrs->numrefs, - behavior); + /* Record all of them (this includes duplicate elimination) */ + lock_record_object_address_dependencies(depender, context.addrs, behavior); free_object_addresses(context.addrs); } @@ -1654,14 +1727,19 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, /* Record the self-dependencies with the appropriate direction */ if (!reverse_self) + { + LockNotPinnedObjectsById(self_addrs->refs, self_addrs->numrefs); recordMultipleDependencies(depender, self_addrs->refs, self_addrs->numrefs, self_behavior); + } else { /* Can't use recordMultipleDependencies, so do it the hard way */ int selfref; + LockNotPinnedObjectById(depender); + for (selfref = 0; selfref < self_addrs->numrefs; selfref++) { ObjectAddress *thisobj = self_addrs->refs + selfref; @@ -1674,6 +1752,7 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, } /* Record the external dependencies */ + LockNotPinnedObjectsById(context.addrs->refs, context.addrs->numrefs); recordMultipleDependencies(depender, context.addrs->refs, context.addrs->numrefs, behavior); @@ -2734,6 +2813,22 @@ stack_address_present_add_flags(const ObjectAddress *object, return result; } +/* + * Record multiple dependencies from an ObjectAddresses array and lock the + * referenced objects, after first removing any duplicates. + */ +void +lock_record_object_address_dependencies(const ObjectAddress *depender, + ObjectAddresses *referenced, + DependencyType behavior) +{ + eliminate_duplicate_dependencies(referenced); + LockNotPinnedObjectsById(referenced->refs, referenced->numrefs); + recordMultipleDependencies(depender, + referenced->refs, referenced->numrefs, + behavior); +} + /* * Record multiple dependencies from an ObjectAddresses array, after first * removing any duplicates. diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index a122bbffce..72443710d3 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -843,6 +843,7 @@ AddNewAttributeTuples(Oid new_rel_oid, ObjectAddressSubSet(myself, RelationRelationId, new_rel_oid, i + 1); ObjectAddressSet(referenced, TypeRelationId, tupdesc->attrs[i].atttypid); + LockNotPinnedObject(TypeRelationId, tupdesc->attrs[i].atttypid); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* The default collation is pinned, so don't bother recording it */ @@ -851,6 +852,7 @@ AddNewAttributeTuples(Oid new_rel_oid, { ObjectAddressSet(referenced, CollationRelationId, tupdesc->attrs[i].attcollation); + LockNotPinnedObject(CollationRelationId, tupdesc->attrs[i].attcollation); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } } @@ -1451,11 +1453,13 @@ heap_create_with_catalog(const char *relname, ObjectAddressSet(referenced, NamespaceRelationId, relnamespace); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(NamespaceRelationId, relnamespace); if (reloftypeid) { ObjectAddressSet(referenced, TypeRelationId, reloftypeid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(TypeRelationId, reloftypeid); } /* @@ -1469,6 +1473,7 @@ heap_create_with_catalog(const char *relname, { ObjectAddressSet(referenced, AccessMethodRelationId, accessmtd); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(AccessMethodRelationId, accessmtd); } record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); @@ -3383,6 +3388,7 @@ StorePartitionKey(Relation rel, { ObjectAddressSet(referenced, OperatorClassRelationId, partopclass[i]); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(OperatorClassRelationId, partopclass[i]); /* The default collation is pinned, so don't bother recording it */ if (OidIsValid(partcollation[i]) && @@ -3390,6 +3396,7 @@ StorePartitionKey(Relation rel, { ObjectAddressSet(referenced, CollationRelationId, partcollation[i]); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(CollationRelationId, partcollation[i]); } } @@ -3409,6 +3416,7 @@ StorePartitionKey(Relation rel, ObjectAddressSubSet(referenced, RelationRelationId, RelationGetRelid(rel), partattrs[i]); + /* Do we need a lock on RelationRelationId? */ recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 55fdde4b24..b89c217a09 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1127,6 +1127,7 @@ index_create(Relation heapRelation, heapRelationId, indexInfo->ii_IndexAttrNumbers[i]); add_exact_object_address(&referenced, addrs); + /* XXX Do we need a lock for RelationRelationId? */ have_simple_col = true; } } @@ -1141,6 +1142,7 @@ index_create(Relation heapRelation, { ObjectAddressSet(referenced, RelationRelationId, heapRelationId); + /* XXX Do we need a lock for RelationRelationId? */ add_exact_object_address(&referenced, addrs); } @@ -1157,9 +1159,11 @@ index_create(Relation heapRelation, if (OidIsValid(parentIndexRelid)) { ObjectAddressSet(referenced, RelationRelationId, parentIndexRelid); + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, heapRelationId); + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC); } @@ -1175,6 +1179,7 @@ index_create(Relation heapRelation, { ObjectAddressSet(referenced, CollationRelationId, collationIds[i]); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(CollationRelationId, collationIds[i]); } } @@ -1183,6 +1188,7 @@ index_create(Relation heapRelation, { ObjectAddressSet(referenced, OperatorClassRelationId, opclassIds[i]); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(OperatorClassRelationId, opclassIds[i]); } record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); @@ -1987,6 +1993,14 @@ index_constraint_create(Relation heapRelation, */ ObjectAddressSet(myself, ConstraintRelationId, conOid); ObjectAddressSet(idxaddr, RelationRelationId, indexRelationId); + + /* + * CommandCounterIncrement here to ensure the new constraint entry is + * visible when we'll check of object existence when recording the + * dependencies. + */ + CommandCounterIncrement(); + LockNotPinnedObject(ConstraintRelationId, conOid); recordDependencyOn(&idxaddr, &myself, DEPENDENCY_INTERNAL); /* @@ -1998,9 +2012,11 @@ index_constraint_create(Relation heapRelation, ObjectAddress referenced; ObjectAddressSet(referenced, ConstraintRelationId, parentConstraintId); + LockNotPinnedObject(ConstraintRelationId, parentConstraintId); recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(heapRelation)); + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC); } diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 7b536ac6fd..6d7abd3738 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -2590,6 +2590,63 @@ get_object_namespace(const ObjectAddress *address) return oid; } +/* + * ObjectByIdExist + * + * Return whether the given object exists. + * + * Works for most catalogs, if no special processing is needed. + */ +bool +ObjectByIdExist(const ObjectAddress *address) +{ + HeapTuple tuple; + int cache; + const ObjectPropertyType *property; + + property = get_object_property_data(address->classId); + + cache = property->oid_catcache_id; + + if (cache >= 0) + { + /* Fetch tuple from syscache. */ + tuple = SearchSysCache1(cache, ObjectIdGetDatum(address->objectId)); + + if (!HeapTupleIsValid(tuple)) + { + return false; + } + + ReleaseSysCache(tuple); + + return true; + } + else + { + Relation rel; + ScanKeyData skey[1]; + SysScanDesc scan; + + rel = table_open(address->classId, AccessShareLock); + + ScanKeyInit(&skey[0], + get_object_attnum_oid(address->classId), + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(address->objectId)); + + scan = systable_beginscan(rel, get_object_oid_index(address->classId), true, + NULL, 1, skey); + + /* we expect exactly one match */ + tuple = systable_getnext(scan); + systable_endscan(scan); + table_close(rel, AccessShareLock); + + return (HeapTupleIsValid(tuple)); + } +} + /* * Return ObjectType for the given object type as given by * getObjectTypeDescription; if no valid ObjectType code exists, but it's a diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 90fc7db949..a47e3c5507 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -748,12 +748,14 @@ AggregateCreate(const char *aggName, /* Depends on transition function */ ObjectAddressSet(referenced, ProcedureRelationId, transfn); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, transfn); /* Depends on final function, if any */ if (OidIsValid(finalfn)) { ObjectAddressSet(referenced, ProcedureRelationId, finalfn); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, finalfn); } /* Depends on combine function, if any */ @@ -761,6 +763,7 @@ AggregateCreate(const char *aggName, { ObjectAddressSet(referenced, ProcedureRelationId, combinefn); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, combinefn); } /* Depends on serialization function, if any */ @@ -768,6 +771,7 @@ AggregateCreate(const char *aggName, { ObjectAddressSet(referenced, ProcedureRelationId, serialfn); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, serialfn); } /* Depends on deserialization function, if any */ @@ -775,6 +779,7 @@ AggregateCreate(const char *aggName, { ObjectAddressSet(referenced, ProcedureRelationId, deserialfn); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, deserialfn); } /* Depends on forward transition function, if any */ @@ -782,6 +787,7 @@ AggregateCreate(const char *aggName, { ObjectAddressSet(referenced, ProcedureRelationId, mtransfn); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, mtransfn); } /* Depends on inverse transition function, if any */ @@ -789,6 +795,7 @@ AggregateCreate(const char *aggName, { ObjectAddressSet(referenced, ProcedureRelationId, minvtransfn); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, minvtransfn); } /* Depends on final function, if any */ @@ -796,6 +803,7 @@ AggregateCreate(const char *aggName, { ObjectAddressSet(referenced, ProcedureRelationId, mfinalfn); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, mfinalfn); } /* Depends on sort operator, if any */ @@ -803,6 +811,7 @@ AggregateCreate(const char *aggName, { ObjectAddressSet(referenced, OperatorRelationId, sortop); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(OperatorRelationId, sortop); } record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); diff --git a/src/backend/catalog/pg_attrdef.c b/src/backend/catalog/pg_attrdef.c index 003ae70b4d..80db2c0c20 100644 --- a/src/backend/catalog/pg_attrdef.c +++ b/src/backend/catalog/pg_attrdef.c @@ -178,6 +178,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, colobject.objectId = RelationGetRelid(rel); colobject.objectSubId = attnum; + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&defobject, &colobject, attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO); diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c index 5a5b855d51..d3707e424c 100644 --- a/src/backend/catalog/pg_cast.c +++ b/src/backend/catalog/pg_cast.c @@ -97,16 +97,19 @@ CastCreate(Oid sourcetypeid, Oid targettypeid, /* dependency on source type */ ObjectAddressSet(referenced, TypeRelationId, sourcetypeid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(TypeRelationId, sourcetypeid); /* dependency on target type */ ObjectAddressSet(referenced, TypeRelationId, targettypeid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(TypeRelationId, targettypeid); /* dependency on function */ if (OidIsValid(funcid)) { ObjectAddressSet(referenced, ProcedureRelationId, funcid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, funcid); } /* dependencies on casts required for function */ @@ -114,11 +117,13 @@ CastCreate(Oid sourcetypeid, Oid targettypeid, { ObjectAddressSet(referenced, CastRelationId, incastid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(CastRelationId, incastid); } if (OidIsValid(outcastid)) { ObjectAddressSet(referenced, CastRelationId, outcastid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(CastRelationId, outcastid); } record_object_address_dependencies(&myself, addrs, behavior); diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c index 7f2f701229..78498b8c20 100644 --- a/src/backend/catalog/pg_collation.c +++ b/src/backend/catalog/pg_collation.c @@ -218,6 +218,7 @@ CollationCreate(const char *collname, Oid collnamespace, referenced.classId = NamespaceRelationId; referenced.objectId = collnamespace; referenced.objectSubId = 0; + LockNotPinnedObject(NamespaceRelationId, collnamespace); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* create dependency on owner */ diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index 3baf9231ed..4afa9a1d69 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -257,12 +257,14 @@ CreateConstraintEntry(const char *constraintName, ObjectAddressSubSet(relobject, RelationRelationId, relId, constraintKey[i]); add_exact_object_address(&relobject, addrs_auto); + /* XXX Do we need a lock for RelationRelationId?? */ } } else { ObjectAddressSet(relobject, RelationRelationId, relId); add_exact_object_address(&relobject, addrs_auto); + /* XXX Do we need a lock for RelationRelationId?? */ } } @@ -275,6 +277,7 @@ CreateConstraintEntry(const char *constraintName, ObjectAddressSet(domobject, TypeRelationId, domainId); add_exact_object_address(&domobject, addrs_auto); + LockNotPinnedObject(TypeRelationId, domainId); } record_object_address_dependencies(&conobject, addrs_auto, @@ -299,12 +302,14 @@ CreateConstraintEntry(const char *constraintName, ObjectAddressSubSet(relobject, RelationRelationId, foreignRelId, foreignKey[i]); add_exact_object_address(&relobject, addrs_normal); + /* XXX Do we need a lock for RelationRelationId? */ } } else { ObjectAddressSet(relobject, RelationRelationId, foreignRelId); add_exact_object_address(&relobject, addrs_normal); + /* XXX Do we need a lock for RelationRelationId? */ } } @@ -320,6 +325,7 @@ CreateConstraintEntry(const char *constraintName, ObjectAddressSet(relobject, RelationRelationId, indexRelId); add_exact_object_address(&relobject, addrs_normal); + /* XXX Do we need a lock for RelationRelationId?? */ } if (foreignNKeys > 0) @@ -339,15 +345,18 @@ CreateConstraintEntry(const char *constraintName, { oprobject.objectId = pfEqOp[i]; add_exact_object_address(&oprobject, addrs_normal); + LockNotPinnedObject(OperatorRelationId, pfEqOp[i]); if (ppEqOp[i] != pfEqOp[i]) { oprobject.objectId = ppEqOp[i]; add_exact_object_address(&oprobject, addrs_normal); + LockNotPinnedObject(OperatorRelationId, ppEqOp[i]); } if (ffEqOp[i] != pfEqOp[i]) { oprobject.objectId = ffEqOp[i]; add_exact_object_address(&oprobject, addrs_normal); + LockNotPinnedObject(OperatorRelationId, ffEqOp[i]); } } } @@ -858,9 +867,11 @@ ConstraintSetParentConstraint(Oid childConstrId, ObjectAddressSet(depender, ConstraintRelationId, childConstrId); ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId); + LockNotPinnedObject(ConstraintRelationId, parentConstrId); recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, childTableId); + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC); } else diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c index 0770878eac..25881654d6 100644 --- a/src/backend/catalog/pg_conversion.c +++ b/src/backend/catalog/pg_conversion.c @@ -116,12 +116,14 @@ ConversionCreate(const char *conname, Oid connamespace, referenced.classId = ProcedureRelationId; referenced.objectId = conproc; referenced.objectSubId = 0; + LockNotPinnedObject(ProcedureRelationId, conproc); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* create dependency on namespace */ referenced.classId = NamespaceRelationId; referenced.objectId = connamespace; referenced.objectSubId = 0; + LockNotPinnedObject(NamespaceRelationId, connamespace); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* create dependency on owner */ diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index cfd7ef51df..b97aa2ab91 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -20,21 +20,20 @@ #include "catalog/catalog.h" #include "catalog/dependency.h" #include "catalog/indexing.h" +#include "catalog/pg_auth_members.h" #include "catalog/pg_constraint.h" #include "catalog/pg_depend.h" #include "catalog/pg_extension.h" #include "catalog/partition.h" #include "commands/extension.h" #include "miscadmin.h" +#include "storage/lock.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/syscache.h" #include "utils/rel.h" -static bool isObjectPinned(const ObjectAddress *object); - - /* * Record a dependency between 2 objects via their respective objectAddress. * The first argument is the dependent object, the second the one it @@ -100,6 +99,25 @@ recordMultipleDependencies(const ObjectAddress *depender, slot_init_count = 0; for (i = 0; i < nreferenced; i++, referenced++) { +#ifdef USE_ASSERT_CHECKING + LOCKTAG tag; + + SET_LOCKTAG_OBJECT(tag, + MyDatabaseId, + referenced->classId, + referenced->objectId, + 0); + + /* + * Assert the referenced object is locked (see + * LockNotPinnedObjectById()). + */ + Assert(isObjectPinned(referenced) || + referenced->classId == RelationRelationId || + referenced->classId == AuthMemRelationId || + LockHeldByMe(&tag, AccessShareLock)); +#endif + /* * If the referenced object is pinned by the system, there's no real * need to record dependencies on it. This saves lots of space in @@ -239,6 +257,7 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object, extension.objectId = CurrentExtensionObject; extension.objectSubId = 0; + LockNotPinnedObject(ExtensionRelationId, CurrentExtensionObject); recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION); } } @@ -706,7 +725,7 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId, * The passed subId, if any, is ignored; we assume that only whole objects * are pinned (and that this implies pinning their components). */ -static bool +bool isObjectPinned(const ObjectAddress *object) { return IsPinnedObject(object->classId, object->objectId); diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 65b45a424a..e8374eec88 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -251,6 +251,16 @@ OperatorShellMake(const char *operatorName, values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid); + /* Lock dependent objects */ + if (OidIsValid(operatorNamespace)) + LockNotPinnedObject(NamespaceRelationId, operatorNamespace); + + if (OidIsValid(leftTypeId)) + LockNotPinnedObject(TypeRelationId, leftTypeId); + + if (OidIsValid(rightTypeId)) + LockNotPinnedObject(TypeRelationId, rightTypeId); + /* * create a new operator tuple */ @@ -513,6 +523,15 @@ OperatorCreate(const char *operatorName, CatalogTupleInsert(pg_operator_desc, tup); } + /* Lock dependent objects */ + LockNotPinnedObject(NamespaceRelationId, operatorNamespace); + LockNotPinnedObject(TypeRelationId, leftTypeId); + LockNotPinnedObject(TypeRelationId, rightTypeId); + LockNotPinnedObject(TypeRelationId, operResultType); + LockNotPinnedObject(ProcedureRelationId, procedureId); + LockNotPinnedObject(ProcedureRelationId, restrictionId); + LockNotPinnedObject(ProcedureRelationId, joinId); + /* Add dependencies for the entry */ address = makeOperatorDependencies(tup, true, isUpdate); diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 528c17cd7f..9cceb6413b 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -593,6 +593,13 @@ ProcedureCreate(const char *procedureName, if (is_update) deleteDependencyRecordsFor(ProcedureRelationId, retval, true); + /* + * CommandCounterIncrement here to ensure the new function entry is + * visible when we'll check of object existence when recording the + * dependencies. + */ + CommandCounterIncrement(); + addrs = new_object_addresses(); ObjectAddressSet(myself, ProcedureRelationId, retval); @@ -600,20 +607,24 @@ ProcedureCreate(const char *procedureName, /* dependency on namespace */ ObjectAddressSet(referenced, NamespaceRelationId, procNamespace); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(NamespaceRelationId, procNamespace); /* dependency on implementation language */ ObjectAddressSet(referenced, LanguageRelationId, languageObjectId); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(LanguageRelationId, languageObjectId); /* dependency on return type */ ObjectAddressSet(referenced, TypeRelationId, returnType); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(TypeRelationId, returnType); /* dependency on transform used by return type, if any */ if ((trfid = get_transform_oid(returnType, languageObjectId, true))) { ObjectAddressSet(referenced, TransformRelationId, trfid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(TransformRelationId, trfid); } /* dependency on parameter types */ @@ -621,12 +632,14 @@ ProcedureCreate(const char *procedureName, { ObjectAddressSet(referenced, TypeRelationId, allParams[i]); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(TypeRelationId, allParams[i]); /* dependency on transform used by parameter type, if any */ if ((trfid = get_transform_oid(allParams[i], languageObjectId, true))) { ObjectAddressSet(referenced, TransformRelationId, trfid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(TransformRelationId, trfid); } } @@ -635,6 +648,7 @@ ProcedureCreate(const char *procedureName, { ObjectAddressSet(referenced, ProcedureRelationId, prosupport); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, prosupport); } record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); @@ -674,9 +688,6 @@ ProcedureCreate(const char *procedureName, ArrayType *set_items = NULL; int save_nestlevel = 0; - /* Advance command counter so new tuple can be seen by validator */ - CommandCounterIncrement(); - /* * Set per-function configuration parameters so that the validation is * done with the environment the function expects. However, if diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c index 0602398a54..5e16c0fcef 100644 --- a/src/backend/catalog/pg_publication.c +++ b/src/backend/catalog/pg_publication.c @@ -438,10 +438,12 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri, /* Add dependency on the publication */ ObjectAddressSet(referenced, PublicationRelationId, pubid); + LockNotPinnedObject(PublicationRelationId, pubid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); /* Add dependency on the relation */ ObjectAddressSet(referenced, RelationRelationId, relid); + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); /* Add dependency on the objects mentioned in the qualifications */ @@ -454,6 +456,7 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri, for (int i = 0; i < natts; i++) { ObjectAddressSubSet(referenced, RelationRelationId, relid, attarray[i]); + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } @@ -661,10 +664,12 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists) /* Add dependency on the publication */ ObjectAddressSet(referenced, PublicationRelationId, pubid); + LockNotPinnedObject(PublicationRelationId, pubid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); /* Add dependency on the schema */ ObjectAddressSet(referenced, NamespaceRelationId, schemaid); + LockNotPinnedObject(NamespaceRelationId, schemaid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); /* Close the table */ diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c index 501a6ba410..e5b5a0b6f8 100644 --- a/src/backend/catalog/pg_range.c +++ b/src/backend/catalog/pg_range.c @@ -70,26 +70,31 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation, ObjectAddressSet(referenced, TypeRelationId, rangeSubType); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(TypeRelationId, rangeSubType); ObjectAddressSet(referenced, OperatorClassRelationId, rangeSubOpclass); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(OperatorClassRelationId, rangeSubOpclass); if (OidIsValid(rangeCollation)) { ObjectAddressSet(referenced, CollationRelationId, rangeCollation); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(CollationRelationId, rangeCollation); } if (OidIsValid(rangeCanonical)) { ObjectAddressSet(referenced, ProcedureRelationId, rangeCanonical); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, rangeCanonical); } if (OidIsValid(rangeSubDiff)) { ObjectAddressSet(referenced, ProcedureRelationId, rangeSubDiff); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, rangeSubDiff); } record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); @@ -99,6 +104,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation, referencing.classId = TypeRelationId; referencing.objectId = multirangeTypeOid; referencing.objectSubId = 0; + LockNotPinnedObject(TypeRelationId, rangeTypeOid); recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL); table_close(pg_range, RowExclusiveLock); diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 395dec8ed8..92614cdaaa 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -157,6 +157,12 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) * Create dependencies. We can/must skip this in bootstrap mode. */ if (!IsBootstrapProcessingMode()) + { + /* Lock dependent objects */ + LockNotPinnedObject(NamespaceRelationId, typeNamespace); + LockNotPinnedObject(ProcedureRelationId, F_SHELL_IN); + LockNotPinnedObject(ProcedureRelationId, F_SHELL_OUT); + GenerateTypeDependencies(tup, pg_type_desc, NULL, @@ -166,6 +172,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) false, true, /* make extension dependency */ false); + } /* Post creation hook for new shell type */ InvokeObjectPostCreateHook(TypeRelationId, typoid, 0); @@ -494,6 +501,31 @@ TypeCreate(Oid newTypeOid, * Create dependencies. We can/must skip this in bootstrap mode. */ if (!IsBootstrapProcessingMode()) + { + /* + * CommandCounterIncrement here to ensure the new type entry is + * visible when we'll check of object existence when recording the + * dependencies. + */ + CommandCounterIncrement(); + + /* Lock dependent objects */ + LockNotPinnedObject(NamespaceRelationId, typeNamespace); + LockNotPinnedObject(ProcedureRelationId, inputProcedure); + LockNotPinnedObject(ProcedureRelationId, outputProcedure); + LockNotPinnedObject(ProcedureRelationId, receiveProcedure); + LockNotPinnedObject(ProcedureRelationId, sendProcedure); + LockNotPinnedObject(ProcedureRelationId, typmodinProcedure); + LockNotPinnedObject(ProcedureRelationId, typmodoutProcedure); + LockNotPinnedObject(ProcedureRelationId, analyzeProcedure); + LockNotPinnedObject(ProcedureRelationId, subscriptProcedure); + LockNotPinnedObject(TypeRelationId, baseType); + LockNotPinnedObject(CollationRelationId, typeCollation); + LockNotPinnedObject(TypeRelationId, elementType); + /* XXX Do we need a lock for RelationRelationId? */ + if (relationKind == RELKIND_COMPOSITE_TYPE) + LockNotPinnedObject(TypeRelationId, typeObjectId); + GenerateTypeDependencies(tup, pg_type_desc, (defaultTypeBin ? @@ -505,6 +537,7 @@ TypeCreate(Oid newTypeOid, isDependentType, true, /* make extension dependency */ rebuildDeps); + } /* Post creation hook for new type */ InvokeObjectPostCreateHook(TypeRelationId, typeObjectId, 0); diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 738bc46ae8..4a708030ac 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -367,6 +367,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, toastobject.objectId = toast_relid; toastobject.objectSubId = 0; + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 4f99ebb447..57e86f576a 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -501,7 +501,10 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre currexts = getAutoExtensionsOfObject(address.classId, address.objectId); if (!list_member_oid(currexts, refAddr.objectId)) + { + LockNotPinnedObject(refAddr.classId, refAddr.objectId); recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION); + } } return address; @@ -807,6 +810,7 @@ AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid) pfree(replaces); /* update dependency to point to the new schema */ + LockNotPinnedObject(NamespaceRelationId, nspOid); if (changeDependencyFor(classId, objid, NamespaceRelationId, oldNspOid, nspOid) != 1) elog(ERROR, "could not change schema dependency for object %u", diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c index aaa0f9a1dc..8616a7c9fa 100644 --- a/src/backend/commands/amcmds.c +++ b/src/backend/commands/amcmds.c @@ -104,6 +104,7 @@ CreateAccessMethod(CreateAmStmt *stmt) referenced.objectId = amhandler; referenced.objectSubId = 0; + LockNotPinnedObject(ProcedureRelationId, amhandler); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOnCurrentExtension(&myself, false); diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 78f96789b0..b54a04f4b0 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -1272,6 +1272,7 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, */ if (relam1 != relam2) { + LockNotPinnedObject(AccessMethodRelationId, relam2); if (changeDependencyFor(RelationRelationId, r1, AccessMethodRelationId, @@ -1280,6 +1281,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, elog(ERROR, "could not change access method dependency for relation \"%s.%s\"", get_namespace_name(get_rel_namespace(r1)), get_rel_name(r1)); + + LockNotPinnedObject(AccessMethodRelationId, relam1); if (changeDependencyFor(RelationRelationId, r2, AccessMethodRelationId, @@ -1381,6 +1384,7 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, { baseobject.objectId = r1; toastobject.objectId = relform1->reltoastrelid; + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } @@ -1389,6 +1393,7 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, { baseobject.objectId = r2; toastobject.objectId = relform2->reltoastrelid; + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 7a5ed6b985..8d0cdec59e 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -327,6 +327,7 @@ insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtO referenced.classId = ProcedureRelationId; referenced.objectId = funcoid; referenced.objectSubId = 0; + LockNotPinnedObject(ProcedureRelationId, funcoid); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* Depend on extension, if any. */ diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 1643c8c69a..669a5d6dd8 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -1924,6 +1924,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner, ObjectAddressSet(nsp, NamespaceRelationId, schemaOid); add_exact_object_address(&nsp, refobjs); + LockNotPinnedObject(NamespaceRelationId, schemaOid); foreach(lc, requiredExtensions) { @@ -1932,6 +1933,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner, ObjectAddressSet(otherext, ExtensionRelationId, reqext); add_exact_object_address(&otherext, refobjs); + LockNotPinnedObject(ExtensionRelationId, reqext); } /* Record all of them (this includes duplicate elimination) */ @@ -2968,6 +2970,7 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o table_close(extRel, RowExclusiveLock); /* update dependency to point to the new schema */ + LockNotPinnedObject(NamespaceRelationId, nspOid); if (changeDependencyFor(ExtensionRelationId, extensionOid, NamespaceRelationId, oldNspOid, nspOid) != 1) elog(ERROR, "could not change schema dependency for extension %s", @@ -3258,6 +3261,7 @@ ApplyExtensionUpdates(Oid extensionOid, otherext.objectId = reqext; otherext.objectSubId = 0; + LockNotPinnedObject(ExtensionRelationId, reqext); recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL); } @@ -3414,6 +3418,7 @@ ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt, /* * OK, add the dependency. */ + LockNotPinnedObject(extension.classId, extension.objectId); recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION); /* diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index cf61bbac1f..735bca486c 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -642,6 +642,7 @@ CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt) referenced.classId = ProcedureRelationId; referenced.objectId = fdwhandler; referenced.objectSubId = 0; + LockNotPinnedObject(ProcedureRelationId, fdwhandler); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } @@ -650,6 +651,7 @@ CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt) referenced.classId = ProcedureRelationId; referenced.objectId = fdwvalidator; referenced.objectSubId = 0; + LockNotPinnedObject(ProcedureRelationId, fdwvalidator); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } @@ -811,6 +813,7 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt) referenced.classId = ProcedureRelationId; referenced.objectId = fdwhandler; referenced.objectSubId = 0; + LockNotPinnedObject(ProcedureRelationId, fdwhandler); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } @@ -819,6 +822,7 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt) referenced.classId = ProcedureRelationId; referenced.objectId = fdwvalidator; referenced.objectSubId = 0; + LockNotPinnedObject(ProcedureRelationId, fdwvalidator); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } } @@ -951,6 +955,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt) referenced.classId = ForeignDataWrapperRelationId; referenced.objectId = fdw->fdwid; referenced.objectSubId = 0; + LockNotPinnedObject(ForeignDataWrapperRelationId, fdw->fdwid); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId); @@ -1195,6 +1200,7 @@ CreateUserMapping(CreateUserMappingStmt *stmt) referenced.classId = ForeignServerRelationId; referenced.objectId = srv->serverid; referenced.objectSubId = 0; + LockNotPinnedObject(ForeignServerRelationId, srv->serverid); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); if (OidIsValid(useId)) @@ -1472,6 +1478,7 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid) referenced.classId = ForeignServerRelationId; referenced.objectId = server->serverid; referenced.objectSubId = 0; + LockNotPinnedObject(ForeignServerRelationId, server->serverid); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); table_close(ftrel, RowExclusiveLock); diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 6593fd7d81..8207ef08b3 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -1446,6 +1446,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt) /* Add or replace dependency on support function */ if (OidIsValid(procForm->prosupport)) { + LockNotPinnedObject(ProcedureRelationId, newsupport); if (changeDependencyFor(ProcedureRelationId, funcOid, ProcedureRelationId, procForm->prosupport, newsupport) != 1) @@ -1459,6 +1460,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt) referenced.classId = ProcedureRelationId; referenced.objectId = newsupport; referenced.objectSubId = 0; + LockNotPinnedObject(ProcedureRelationId, newsupport); recordDependencyOn(&address, &referenced, DEPENDENCY_NORMAL); } @@ -1962,21 +1964,25 @@ CreateTransform(CreateTransformStmt *stmt) /* dependency on language */ ObjectAddressSet(referenced, LanguageRelationId, langid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(LanguageRelationId, langid); /* dependency on type */ ObjectAddressSet(referenced, TypeRelationId, typeid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(TypeRelationId, typeid); /* dependencies on functions */ if (OidIsValid(fromsqlfuncid)) { ObjectAddressSet(referenced, ProcedureRelationId, fromsqlfuncid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, fromsqlfuncid); } if (OidIsValid(tosqlfuncid)) { ObjectAddressSet(referenced, ProcedureRelationId, tosqlfuncid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, tosqlfuncid); } record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 309389e20d..b14eaad7a3 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -4377,8 +4377,10 @@ IndexSetParentIndex(Relation partitionIdx, Oid parentOid) ObjectAddressSet(parentIdx, RelationRelationId, parentOid); ObjectAddressSet(partitionTbl, RelationRelationId, partitionIdx->rd_index->indrelid); + /* Do we lock for RelationRelationId?? */ recordDependencyOn(&partIdx, &parentIdx, DEPENDENCY_PARTITION_PRI); + /* Do we lock for RelationRelationId?? */ recordDependencyOn(&partIdx, &partitionTbl, DEPENDENCY_PARTITION_SEC); } diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index b8b5c147c5..ca15106ca9 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -298,12 +298,14 @@ CreateOpFamily(CreateOpFamilyStmt *stmt, const char *opfname, referenced.classId = AccessMethodRelationId; referenced.objectId = amoid; referenced.objectSubId = 0; + LockNotPinnedObject(AccessMethodRelationId, amoid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); /* dependency on namespace */ referenced.classId = NamespaceRelationId; referenced.objectId = namespaceoid; referenced.objectSubId = 0; + LockNotPinnedObject(NamespaceRelationId, namespaceoid); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on owner */ @@ -725,18 +727,21 @@ DefineOpClass(CreateOpClassStmt *stmt) referenced.classId = NamespaceRelationId; referenced.objectId = namespaceoid; referenced.objectSubId = 0; + LockNotPinnedObject(NamespaceRelationId, namespaceoid); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on opfamily */ referenced.classId = OperatorFamilyRelationId; referenced.objectId = opfamilyoid; referenced.objectSubId = 0; + LockNotPinnedObject(OperatorFamilyRelationId, opfamilyoid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); /* dependency on indexed datatype */ referenced.classId = TypeRelationId; referenced.objectId = typeoid; referenced.objectSubId = 0; + LockNotPinnedObject(TypeRelationId, typeoid); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on storage datatype */ @@ -745,6 +750,7 @@ DefineOpClass(CreateOpClassStmt *stmt) referenced.classId = TypeRelationId; referenced.objectId = storageoid; referenced.objectSubId = 0; + LockNotPinnedObject(TypeRelationId, storageoid); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } @@ -1486,6 +1492,13 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, heap_freetuple(tup); + /* + * CommandCounterIncrement here to ensure the new operator entry is + * visible when we'll check of object existence when recording the + * dependencies. + */ + CommandCounterIncrement(); + /* Make its dependencies */ myself.classId = AccessMethodOperatorRelationId; myself.objectId = entryoid; @@ -1496,6 +1509,7 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, referenced.objectSubId = 0; /* see comments in amapi.h about dependency strength */ + LockNotPinnedObject(OperatorRelationId, op->object); recordDependencyOn(&myself, &referenced, op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO); @@ -1504,6 +1518,7 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, referenced.objectId = op->refobjid; referenced.objectSubId = 0; + LockNotPinnedObject(referenced.classId, op->refobjid); recordDependencyOn(&myself, &referenced, op->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO); @@ -1514,6 +1529,7 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, referenced.objectId = op->sortfamily; referenced.objectSubId = 0; + LockNotPinnedObject(OperatorFamilyRelationId, op->sortfamily); recordDependencyOn(&myself, &referenced, op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO); } @@ -1597,6 +1613,7 @@ storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid, referenced.objectSubId = 0; /* see comments in amapi.h about dependency strength */ + LockNotPinnedObject(ProcedureRelationId, proc->object); recordDependencyOn(&myself, &referenced, proc->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO); @@ -1605,6 +1622,7 @@ storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid, referenced.objectId = proc->refobjid; referenced.objectSubId = 0; + LockNotPinnedObject(referenced.classId, proc->refobjid); recordDependencyOn(&myself, &referenced, proc->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO); diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index 5872a3e192..58a69e7cc2 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -33,6 +33,7 @@ #include "access/htup_details.h" #include "access/table.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/objectaccess.h" #include "catalog/pg_namespace.h" @@ -656,11 +657,15 @@ AlterOperator(AlterOperatorStmt *stmt) { replaces[Anum_pg_operator_oprrest - 1] = true; values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionOid); + if (OidIsValid(restrictionOid)) + LockNotPinnedObject(ProcedureRelationId, restrictionOid); } if (updateJoin) { replaces[Anum_pg_operator_oprjoin - 1] = true; values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinOid); + if (OidIsValid(joinOid)) + LockNotPinnedObject(ProcedureRelationId, joinOid); } if (OidIsValid(commutatorOid)) { @@ -688,6 +693,31 @@ AlterOperator(AlterOperatorStmt *stmt) CatalogTupleUpdate(catalog, &tup->t_self, tup); + + /* Lock dependent objects */ + oprForm = (Form_pg_operator) GETSTRUCT(tup); + + if (OidIsValid(oprForm->oprnamespace)) + LockNotPinnedObject(NamespaceRelationId, oprForm->oprnamespace); + + if (OidIsValid(oprForm->oprleft)) + LockNotPinnedObject(TypeRelationId, oprForm->oprleft); + + if (OidIsValid(oprForm->oprright)) + LockNotPinnedObject(TypeRelationId, oprForm->oprright); + + if (OidIsValid(oprForm->oprresult)) + LockNotPinnedObject(TypeRelationId, oprForm->oprresult); + + if (OidIsValid(oprForm->oprcode)) + LockNotPinnedObject(ProcedureRelationId, oprForm->oprcode); + + if (OidIsValid(oprForm->oprrest)) + LockNotPinnedObject(ProcedureRelationId, oprForm->oprrest); + + if (OidIsValid(oprForm->oprjoin)) + LockNotPinnedObject(ProcedureRelationId, oprForm->oprjoin); + address = makeOperatorDependencies(tup, false, true); if (OidIsValid(commutatorOid) || OidIsValid(negatorOid)) diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c index 6ff3eba824..31e61e4e67 100644 --- a/src/backend/commands/policy.c +++ b/src/backend/commands/policy.c @@ -722,6 +722,7 @@ CreatePolicy(CreatePolicyStmt *stmt) myself.objectId = policy_id; myself.objectSubId = 0; + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &target, DEPENDENCY_AUTO); recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable, @@ -1053,6 +1054,7 @@ AlterPolicy(AlterPolicyStmt *stmt) myself.objectId = policy_id; myself.objectSubId = 0; + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &target, DEPENDENCY_AUTO); recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL); diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 881f90017e..fadfd9064f 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -190,12 +190,14 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) /* dependency on the PL handler function */ ObjectAddressSet(referenced, ProcedureRelationId, handlerOid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, handlerOid); /* dependency on the inline handler function, if any */ if (OidIsValid(inlineOid)) { ObjectAddressSet(referenced, ProcedureRelationId, inlineOid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, inlineOid); } /* dependency on the validator function, if any */ @@ -203,6 +205,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) { ObjectAddressSet(referenced, ProcedureRelationId, valOid); add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(ProcedureRelationId, valOid); } record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 28f8522264..b1db4e8933 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1681,6 +1681,7 @@ process_owned_by(Relation seqrel, List *owned_by, bool for_identity) depobject.classId = RelationRelationId; depobject.objectId = RelationGetRelid(seqrel); depobject.objectSubId = 0; + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&depobject, &refobject, deptype); } diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c index 1db3ef69d2..372ae02650 100644 --- a/src/backend/commands/statscmds.c +++ b/src/backend/commands/statscmds.c @@ -536,6 +536,7 @@ CreateStatistics(CreateStatsStmt *stmt) for (i = 0; i < nattnums; i++) { ObjectAddressSubSet(parentobject, RelationRelationId, relid, attnums[i]); + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO); } @@ -553,6 +554,7 @@ CreateStatistics(CreateStatsStmt *stmt) if (!nattnums) { ObjectAddressSet(parentobject, RelationRelationId, relid); + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO); } @@ -573,6 +575,7 @@ CreateStatistics(CreateStatsStmt *stmt) * than the underlying table(s). */ ObjectAddressSet(parentobject, NamespaceRelationId, namespaceId); + LockNotPinnedObject(NamespaceRelationId, namespaceId); recordDependencyOn(&myself, &parentobject, DEPENDENCY_NORMAL); recordDependencyOnOwner(StatisticExtRelationId, statoid, stxowner); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 66cda26a25..c66f110984 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -3438,6 +3438,7 @@ StoreCatalogInheritance1(Oid relationId, Oid parentOid, childobject.objectId = relationId; childobject.objectSubId = 0; + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&childobject, &parentobject, child_dependency_type(child_is_partition)); @@ -7349,7 +7350,9 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, /* * Add needed dependency entries for the new column. */ + LockNotPinnedObject(TypeRelationId, attribute->atttypid); add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid); + LockNotPinnedObject(CollationRelationId, attribute->attcollation); add_column_collation_dependency(myrelid, newattnum, attribute->attcollation); /* @@ -10174,6 +10177,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, ObjectAddress referenced; ObjectAddressSet(referenced, ConstraintRelationId, parentConstr); + LockNotPinnedObject(ConstraintRelationId, parentConstr); recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL); } @@ -10465,8 +10469,10 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, */ ObjectAddressSet(address, ConstraintRelationId, constrOid); ObjectAddressSet(referenced, ConstraintRelationId, parentConstr); + LockNotPinnedObject(ConstraintRelationId, parentConstr); recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, partitionId); + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC); /* Make all this visible before recursing */ @@ -10967,9 +10973,11 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) /* Set up partition dependencies for the new constraint */ ObjectAddressSet(address, ConstraintRelationId, constrOid); ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid); + LockDatabaseObject(ConstraintRelationId, parentConstrOid, 0, AccessShareLock); recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(partRel)); + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC); /* Done with the cloned constraint's tuple */ @@ -13254,7 +13262,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, table_close(attrelation, RowExclusiveLock); /* Install dependencies on new datatype and collation */ + LockNotPinnedObject(TypeRelationId, targettype); add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype); + LockNotPinnedObject(CollationRelationId, targetcollid); add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid); /* @@ -14816,6 +14826,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId) */ ObjectAddressSet(relobj, RelationRelationId, reloid); ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam); + LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam); recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL); } else if (OidIsValid(oldAccessMethodId) && @@ -14835,6 +14846,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId) OidIsValid(rd_rel->relam)); /* Both are valid, so update the dependency */ + LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam); changeDependencyFor(RelationRelationId, reloid, AccessMethodRelationId, oldAccessMethodId, rd_rel->relam); @@ -16434,6 +16446,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode) typeobj.classId = TypeRelationId; typeobj.objectId = typeid; typeobj.objectSubId = 0; + LockNotPinnedObject(TypeRelationId, typeid); recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL); /* Update pg_class.reloftype */ @@ -17192,14 +17205,17 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid, CatalogTupleUpdate(classRel, &classTup->t_self, classTup); /* Update dependency on schema if caller said so */ - if (hasDependEntry && - changeDependencyFor(RelationRelationId, - relOid, - NamespaceRelationId, - oldNspOid, - newNspOid) != 1) - elog(ERROR, "could not change schema dependency for relation \"%s\"", - NameStr(classForm->relname)); + if (hasDependEntry) + { + LockNotPinnedObject(NamespaceRelationId, newNspOid); + if (changeDependencyFor(RelationRelationId, + relOid, + NamespaceRelationId, + oldNspOid, + newNspOid) != 1) + elog(ERROR, "could not change schema dependency for relation \"%s\"", + NameStr(classForm->relname)); + } } if (!already_done) { diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 95de402fa6..44b15349b3 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -1018,8 +1018,6 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, ((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true; CatalogTupleUpdate(pgrel, &tuple->t_self, tuple); - - CommandCounterIncrement(); } else CacheInvalidateRelcacheByTuple(tuple); @@ -1027,6 +1025,12 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, heap_freetuple(tuple); table_close(pgrel, RowExclusiveLock); + /* + * CommandCounterIncrement here to ensure the new trigger entry is visible + * when we'll check of object existence when recording the dependencies. + */ + CommandCounterIncrement(); + /* * If we're replacing a trigger, flush all the old dependencies before * recording new ones. @@ -1045,6 +1049,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, referenced.classId = ProcedureRelationId; referenced.objectId = funcoid; referenced.objectSubId = 0; + LockNotPinnedObject(ProcedureRelationId, funcoid); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); if (isInternal && OidIsValid(constraintOid)) @@ -1058,6 +1063,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, referenced.classId = ConstraintRelationId; referenced.objectId = constraintOid; referenced.objectSubId = 0; + LockNotPinnedObject(ConstraintRelationId, constraintOid); recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); } else @@ -1070,6 +1076,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, referenced.classId = RelationRelationId; referenced.objectId = RelationGetRelid(rel); referenced.objectSubId = 0; + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); if (OidIsValid(constrrelid)) @@ -1077,6 +1084,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, referenced.classId = RelationRelationId; referenced.objectId = constrrelid; referenced.objectSubId = 0; + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); } /* Not possible to have an index dependency in this case */ @@ -1091,6 +1099,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, referenced.classId = ConstraintRelationId; referenced.objectId = constraintOid; referenced.objectSubId = 0; + LockNotPinnedObject(TriggerRelationId, trigoid); recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); } @@ -1100,8 +1109,10 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, if (OidIsValid(parentTriggerOid)) { ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid); + LockNotPinnedObject(TriggerRelationId, parentTriggerOid); recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel)); + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC); } } @@ -1116,6 +1127,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, for (i = 0; i < ncolumns; i++) { referenced.objectSubId = columns[i]; + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } } @@ -1255,9 +1267,11 @@ TriggerSetParentTrigger(Relation trigRel, ObjectAddressSet(depender, TriggerRelationId, childTrigId); ObjectAddressSet(referenced, TriggerRelationId, parentTrigId); + LockNotPinnedObject(TriggerRelationId, parentTrigId); recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, childTableId); + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC); } else diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c index b7b5019f1e..29e02f4946 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -66,12 +66,12 @@ static DefElem *buildDefItem(const char *name, const char *val, /* --------------------- TS Parser commands ------------------------ */ /* - * lookup a parser support function and return its OID (as a Datum) + * lookup a parser support function and return its OID * * attnum is the pg_ts_parser column the function will go into */ -static Datum -get_ts_parser_func(DefElem *defel, int attnum) +static Oid +get_ts_parser_func_oid(DefElem *defel, int attnum) { List *funcName = defGetQualifiedName(defel); Oid typeId[3]; @@ -125,7 +125,7 @@ get_ts_parser_func(DefElem *defel, int attnum) func_signature_string(funcName, nargs, NIL, typeId), format_type_be(retTypeId)))); - return ObjectIdGetDatum(procOid); + return procOid; } /* @@ -214,6 +214,7 @@ DefineTSParser(List *names, List *parameters) namestrcpy(&pname, prsname); values[Anum_pg_ts_parser_prsname - 1] = NameGetDatum(&pname); values[Anum_pg_ts_parser_prsnamespace - 1] = ObjectIdGetDatum(namespaceoid); + LockNotPinnedObject(NamespaceRelationId, namespaceoid); /* * loop over the definition list and extract the information we need. @@ -224,28 +225,38 @@ DefineTSParser(List *names, List *parameters) if (strcmp(defel->defname, "start") == 0) { - values[Anum_pg_ts_parser_prsstart - 1] = - get_ts_parser_func(defel, Anum_pg_ts_parser_prsstart); + Oid procoid = get_ts_parser_func_oid(defel, Anum_pg_ts_parser_prsstart); + + values[Anum_pg_ts_parser_prsstart - 1] = ObjectIdGetDatum(procoid); + LockNotPinnedObject(ProcedureRelationId, procoid); } else if (strcmp(defel->defname, "gettoken") == 0) { - values[Anum_pg_ts_parser_prstoken - 1] = - get_ts_parser_func(defel, Anum_pg_ts_parser_prstoken); + Oid procoid = get_ts_parser_func_oid(defel, Anum_pg_ts_parser_prstoken); + + values[Anum_pg_ts_parser_prstoken - 1] = ObjectIdGetDatum(procoid); + LockNotPinnedObject(ProcedureRelationId, procoid); } else if (strcmp(defel->defname, "end") == 0) { - values[Anum_pg_ts_parser_prsend - 1] = - get_ts_parser_func(defel, Anum_pg_ts_parser_prsend); + Oid procoid = get_ts_parser_func_oid(defel, Anum_pg_ts_parser_prsend); + + values[Anum_pg_ts_parser_prsend - 1] = ObjectIdGetDatum(procoid); + LockNotPinnedObject(ProcedureRelationId, procoid); } else if (strcmp(defel->defname, "headline") == 0) { - values[Anum_pg_ts_parser_prsheadline - 1] = - get_ts_parser_func(defel, Anum_pg_ts_parser_prsheadline); + Oid procoid = get_ts_parser_func_oid(defel, Anum_pg_ts_parser_prsheadline); + + values[Anum_pg_ts_parser_prsheadline - 1] = ObjectIdGetDatum(procoid); + LockNotPinnedObject(ProcedureRelationId, procoid); } else if (strcmp(defel->defname, "lextypes") == 0) { - values[Anum_pg_ts_parser_prslextype - 1] = - get_ts_parser_func(defel, Anum_pg_ts_parser_prslextype); + Oid procoid = get_ts_parser_func_oid(defel, Anum_pg_ts_parser_prslextype); + + values[Anum_pg_ts_parser_prslextype - 1] = ObjectIdGetDatum(procoid); + LockNotPinnedObject(ProcedureRelationId, procoid); } else ereport(ERROR, @@ -474,6 +485,10 @@ DefineTSDictionary(List *names, List *parameters) CatalogTupleInsert(dictRel, tup); + /* Lock objects */ + LockNotPinnedObject(NamespaceRelationId, namespaceoid); + LockNotPinnedObject(TSTemplateRelationId, templId); + address = makeDictionaryDependencies(tup); /* Post creation hook for new text search dictionary */ @@ -601,12 +616,12 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt) /* ---------------------- TS Template commands -----------------------*/ /* - * lookup a template support function and return its OID (as a Datum) + * lookup a template support function and return its OID * * attnum is the pg_ts_template column the function will go into */ -static Datum -get_ts_template_func(DefElem *defel, int attnum) +static Oid +get_ts_template_func_oid(DefElem *defel, int attnum) { List *funcName = defGetQualifiedName(defel); Oid typeId[4]; @@ -642,7 +657,7 @@ get_ts_template_func(DefElem *defel, int attnum) func_signature_string(funcName, nargs, NIL, typeId), format_type_be(retTypeId)))); - return ObjectIdGetDatum(procOid); + return procOid; } /* @@ -723,6 +738,7 @@ DefineTSTemplate(List *names, List *parameters) namestrcpy(&dname, tmplname); values[Anum_pg_ts_template_tmplname - 1] = NameGetDatum(&dname); values[Anum_pg_ts_template_tmplnamespace - 1] = ObjectIdGetDatum(namespaceoid); + LockNotPinnedObject(NamespaceRelationId, namespaceoid); /* * loop over the definition list and extract the information we need. @@ -733,15 +749,19 @@ DefineTSTemplate(List *names, List *parameters) if (strcmp(defel->defname, "init") == 0) { - values[Anum_pg_ts_template_tmplinit - 1] = - get_ts_template_func(defel, Anum_pg_ts_template_tmplinit); + Oid procoid = get_ts_template_func_oid(defel, Anum_pg_ts_template_tmplinit); + + values[Anum_pg_ts_template_tmplinit - 1] = ObjectIdGetDatum(procoid); nulls[Anum_pg_ts_template_tmplinit - 1] = false; + LockNotPinnedObject(ProcedureRelationId, procoid); } else if (strcmp(defel->defname, "lexize") == 0) { - values[Anum_pg_ts_template_tmpllexize - 1] = - get_ts_template_func(defel, Anum_pg_ts_template_tmpllexize); + Oid procoid = get_ts_template_func_oid(defel, Anum_pg_ts_template_tmpllexize); + + values[Anum_pg_ts_template_tmpllexize - 1] = ObjectIdGetDatum(procoid); nulls[Anum_pg_ts_template_tmpllexize - 1] = false; + LockNotPinnedObject(ProcedureRelationId, procoid); } else ereport(ERROR, @@ -879,6 +899,7 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld, referenced.objectId = cfgmap->mapdict; referenced.objectSubId = 0; add_exact_object_address(&referenced, addrs); + LockNotPinnedObject(TSDictionaryRelationId, cfgmap->mapdict); } systable_endscan(scan); @@ -998,6 +1019,10 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied) values[Anum_pg_ts_config_cfgowner - 1] = ObjectIdGetDatum(GetUserId()); values[Anum_pg_ts_config_cfgparser - 1] = ObjectIdGetDatum(prsOid); + /* Lock objects */ + LockNotPinnedObject(NamespaceRelationId, namespaceoid); + LockNotPinnedObject(TSParserRelationId, prsOid); + tup = heap_form_tuple(cfgRel->rd_att, values, nulls); CatalogTupleInsert(cfgRel, tup); @@ -1156,6 +1181,7 @@ ObjectAddress AlterTSConfiguration(AlterTSConfigurationStmt *stmt) { HeapTuple tup; + Form_pg_ts_config cfg; Oid cfgId; Relation relMap; ObjectAddress address; @@ -1168,7 +1194,8 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt) errmsg("text search configuration \"%s\" does not exist", NameListToString(stmt->cfgname)))); - cfgId = ((Form_pg_ts_config) GETSTRUCT(tup))->oid; + cfg = (Form_pg_ts_config) GETSTRUCT(tup); + cfgId = cfg->oid; /* must be owner */ if (!object_ownercheck(TSConfigRelationId, cfgId, GetUserId())) @@ -1183,6 +1210,10 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt) else if (stmt->tokentype) DropConfigurationMapping(stmt, tup, relMap); + /* Lock dependent objects */ + LockNotPinnedObject(NamespaceRelationId, cfg->cfgnamespace); + LockNotPinnedObject(TSParserRelationId, cfg->cfgparser); + /* Update dependencies */ makeConfigurationDependencies(tup, true, relMap); @@ -1414,6 +1445,8 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, repl_val[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictNew); repl_repl[Anum_pg_ts_config_map_mapdict - 1] = true; + LockNotPinnedObject(TSDictionaryRelationId, dictNew); + newtup = heap_modify_tuple(maptup, RelationGetDescr(relMap), repl_val, repl_null, repl_repl); @@ -1456,6 +1489,8 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1); slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]); + LockNotPinnedObject(TSDictionaryRelationId, dictIds[j]); + ExecStoreVirtualTuple(slot[slotCount]); slotCount++; diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 2a1e713335..9febaa24a7 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -1794,6 +1794,7 @@ makeRangeConstructors(const char *name, Oid namespace, * that they go away silently when the type is dropped. Note that * pg_dump depends on this choice to avoid dumping the constructors. */ + LockNotPinnedObject(TypeRelationId, rangeOid); recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); } } @@ -1859,6 +1860,7 @@ makeMultirangeConstructors(const char *name, Oid namespace, * that they go away silently when the type is dropped. Note that pg_dump * depends on this choice to avoid dumping the constructors. */ + LockNotPinnedObject(TypeRelationId, multirangeOid); recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); pfree(argtypes); @@ -2672,6 +2674,45 @@ AlterDomainDefault(List *names, Node *defaultRaw) CatalogTupleUpdate(rel, &tup->t_self, newtuple); + /* Lock dependent objects */ + typTup = (Form_pg_type) GETSTRUCT(newtuple); + + if (OidIsValid(typTup->typnamespace)) + LockNotPinnedObject(NamespaceRelationId, typTup->typnamespace); + + if (OidIsValid(typTup->typinput)) + LockNotPinnedObject(ProcedureRelationId, typTup->typinput); + + if (OidIsValid(typTup->typoutput)) + LockNotPinnedObject(ProcedureRelationId, typTup->typoutput); + + if (OidIsValid(typTup->typreceive)) + LockNotPinnedObject(ProcedureRelationId, typTup->typreceive); + + if (OidIsValid(typTup->typsend)) + LockNotPinnedObject(ProcedureRelationId, typTup->typsend); + + if (OidIsValid(typTup->typmodin)) + LockNotPinnedObject(ProcedureRelationId, typTup->typmodin); + + if (OidIsValid(typTup->typmodout)) + LockNotPinnedObject(ProcedureRelationId, typTup->typmodout); + + if (OidIsValid(typTup->typanalyze)) + LockNotPinnedObject(ProcedureRelationId, typTup->typanalyze); + + if (OidIsValid(typTup->typsubscript)) + LockNotPinnedObject(ProcedureRelationId, typTup->typsubscript); + + if (OidIsValid(typTup->typbasetype)) + LockNotPinnedObject(TypeRelationId, typTup->typbasetype); + + if (OidIsValid(typTup->typcollation)) + LockNotPinnedObject(CollationRelationId, typTup->typcollation); + + if (OidIsValid(typTup->typelem)) + LockNotPinnedObject(TypeRelationId, typTup->typelem); + /* Rebuild dependencies */ GenerateTypeDependencies(newtuple, rel, @@ -4276,10 +4317,13 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, if (oldNspOid != nspOid && (isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) && !isImplicitArray) + { + LockNotPinnedObject(NamespaceRelationId, nspOid); if (changeDependencyFor(TypeRelationId, typeOid, NamespaceRelationId, oldNspOid, nspOid) != 1) elog(ERROR, "could not change schema dependency for type \"%s\"", format_type_be(typeOid)); + } InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0); @@ -4571,6 +4615,7 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray, SysScanDesc scan; ScanKeyData key[1]; HeapTuple domainTup; + Form_pg_type typeForm; /* Since this function recurses, it could be driven to stack overflow */ check_stack_depth(); @@ -4619,6 +4664,45 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray, newtup = heap_modify_tuple(tup, RelationGetDescr(catalog), values, nulls, replaces); + /* Lock dependent objects */ + typeForm = (Form_pg_type) GETSTRUCT(newtup); + + if (OidIsValid(typeForm->typnamespace)) + LockNotPinnedObject(NamespaceRelationId, typeForm->typnamespace); + + if (OidIsValid(typeForm->typinput)) + LockNotPinnedObject(ProcedureRelationId, typeForm->typinput); + + if (OidIsValid(typeForm->typoutput)) + LockNotPinnedObject(ProcedureRelationId, typeForm->typoutput); + + if (OidIsValid(typeForm->typreceive)) + LockNotPinnedObject(ProcedureRelationId, typeForm->typreceive); + + if (OidIsValid(typeForm->typsend)) + LockNotPinnedObject(ProcedureRelationId, typeForm->typsend); + + if (OidIsValid(typeForm->typmodin)) + LockNotPinnedObject(ProcedureRelationId, typeForm->typmodin); + + if (OidIsValid(typeForm->typmodout)) + LockNotPinnedObject(ProcedureRelationId, typeForm->typmodout); + + if (OidIsValid(typeForm->typanalyze)) + LockNotPinnedObject(ProcedureRelationId, typeForm->typanalyze); + + if (OidIsValid(typeForm->typsubscript)) + LockNotPinnedObject(ProcedureRelationId, typeForm->typsubscript); + + if (OidIsValid(typeForm->typbasetype)) + LockNotPinnedObject(TypeRelationId, typeForm->typbasetype); + + if (OidIsValid(typeForm->typcollation)) + LockNotPinnedObject(CollationRelationId, typeForm->typcollation); + + if (OidIsValid(typeForm->typelem)) + LockNotPinnedObject(TypeRelationId, typeForm->typelem); + CatalogTupleUpdate(catalog, &newtup->t_self, newtup); /* Rebuild dependencies for this type */ diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 6cc9a8d8bf..ccd03ceeb8 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -155,6 +155,7 @@ InsertRule(const char *rulname, referenced.objectId = eventrel_oid; referenced.objectSubId = 0; + /* XXX Do we need a lock for RelationRelationId? */ recordDependencyOn(&myself, &referenced, (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO); diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt index 3250d539e1..60e8539fe3 100644 --- a/src/backend/utils/errcodes.txt +++ b/src/backend/utils/errcodes.txt @@ -271,6 +271,7 @@ Section: Class 28 - Invalid Authorization Specification Section: Class 2B - Dependent Privilege Descriptors Still Exist 2B000 E ERRCODE_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST dependent_privilege_descriptors_still_exist +2BP02 E ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST dependent_objects_does_not_exist 2BP01 E ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST dependent_objects_still_exist Section: Class 2D - Invalid Transaction Termination diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 7eee66f810..c57204cc40 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -101,6 +101,9 @@ typedef struct ObjectAddresses ObjectAddresses; /* in dependency.c */ extern void AcquireDeletionLock(const ObjectAddress *object, int flags); +extern void LockNotPinnedObjectById(const ObjectAddress *object); +extern void LockNotPinnedObjectsById(const ObjectAddress *object, int nobject); +extern void LockNotPinnedObject(Oid classid, Oid objid); extern void ReleaseDeletionLock(const ObjectAddress *object); @@ -128,6 +131,9 @@ extern void add_exact_object_address(const ObjectAddress *object, extern bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs); +extern void lock_record_object_address_dependencies(const ObjectAddress *depender, + ObjectAddresses *referenced, + DependencyType behavior); extern void record_object_address_dependencies(const ObjectAddress *depender, ObjectAddresses *referenced, DependencyType behavior); @@ -172,6 +178,7 @@ extern long changeDependenciesOf(Oid classId, Oid oldObjectId, extern long changeDependenciesOn(Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId); +extern bool isObjectPinned(const ObjectAddress *object); extern Oid getExtensionOfObject(Oid classId, Oid objectId); extern List *getAutoExtensionsOfObject(Oid classId, Oid objectId); diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h index 3a70d80e32..56f746264b 100644 --- a/src/include/catalog/objectaddress.h +++ b/src/include/catalog/objectaddress.h @@ -53,6 +53,7 @@ extern void check_object_ownership(Oid roleid, Node *object, Relation relation); extern Oid get_object_namespace(const ObjectAddress *address); +extern bool ObjectByIdExist(const ObjectAddress *address); extern bool is_objectclass_supported(Oid class_id); extern const char *get_object_class_descr(Oid class_id); diff --git a/src/test/isolation/expected/test_dependencies_locks.out b/src/test/isolation/expected/test_dependencies_locks.out new file mode 100644 index 0000000000..9b645d7aa5 --- /dev/null +++ b/src/test/isolation/expected/test_dependencies_locks.out @@ -0,0 +1,129 @@ +Parsed test spec with 2 sessions + +starting permutation: s1_begin s1_create_function_in_schema s2_drop_schema s1_commit +step s1_begin: BEGIN; +step s1_create_function_in_schema: CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; +step s2_drop_schema: DROP SCHEMA testschema; +step s1_commit: COMMIT; +step s2_drop_schema: <... completed> +ERROR: cannot drop schema testschema because other objects depend on it + +starting permutation: s2_begin s2_drop_schema s1_create_function_in_schema s2_commit +step s2_begin: BEGIN; +step s2_drop_schema: DROP SCHEMA testschema; +step s1_create_function_in_schema: CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; +step s2_commit: COMMIT; +step s1_create_function_in_schema: <... completed> +ERROR: schema testschema does not exist + +starting permutation: s1_begin s1_alter_function_schema s2_drop_alterschema s1_commit +step s1_begin: BEGIN; +step s1_alter_function_schema: ALTER FUNCTION public.falter() SET SCHEMA alterschema; +step s2_drop_alterschema: DROP SCHEMA alterschema; +step s1_commit: COMMIT; +step s2_drop_alterschema: <... completed> +ERROR: cannot drop schema alterschema because other objects depend on it + +starting permutation: s2_begin s2_drop_alterschema s1_alter_function_schema s2_commit +step s2_begin: BEGIN; +step s2_drop_alterschema: DROP SCHEMA alterschema; +step s1_alter_function_schema: ALTER FUNCTION public.falter() SET SCHEMA alterschema; +step s2_commit: COMMIT; +step s1_alter_function_schema: <... completed> +ERROR: schema alterschema does not exist + +starting permutation: s1_begin s1_create_function_with_argtype s2_drop_foo_type s1_commit +step s1_begin: BEGIN; +step s1_create_function_with_argtype: CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; +step s2_drop_foo_type: DROP TYPE public.foo; +step s1_commit: COMMIT; +step s2_drop_foo_type: <... completed> +ERROR: cannot drop type foo because other objects depend on it + +starting permutation: s2_begin s2_drop_foo_type s1_create_function_with_argtype s2_commit +step s2_begin: BEGIN; +step s2_drop_foo_type: DROP TYPE public.foo; +step s1_create_function_with_argtype: CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; +step s2_commit: COMMIT; +step s1_create_function_with_argtype: <... completed> +ERROR: type foo does not exist + +starting permutation: s1_begin s1_create_function_with_rettype s2_drop_foo_rettype s1_commit +step s1_begin: BEGIN; +step s1_create_function_with_rettype: CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1; +step s2_drop_foo_rettype: DROP DOMAIN id; +step s1_commit: COMMIT; +step s2_drop_foo_rettype: <... completed> +ERROR: cannot drop type id because other objects depend on it + +starting permutation: s2_begin s2_drop_foo_rettype s1_create_function_with_rettype s2_commit +step s2_begin: BEGIN; +step s2_drop_foo_rettype: DROP DOMAIN id; +step s1_create_function_with_rettype: CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1; +step s2_commit: COMMIT; +step s1_create_function_with_rettype: <... completed> +ERROR: type id does not exist + +starting permutation: s1_begin s1_create_function_with_function s2_drop_function_f s1_commit +step s1_begin: BEGIN; +step s1_create_function_with_function: CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1; +step s2_drop_function_f: DROP FUNCTION f(); +step s1_commit: COMMIT; +step s2_drop_function_f: <... completed> +ERROR: cannot drop function f() because other objects depend on it + +starting permutation: s2_begin s2_drop_function_f s1_create_function_with_function s2_commit +step s2_begin: BEGIN; +step s2_drop_function_f: DROP FUNCTION f(); +step s1_create_function_with_function: CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1; +step s2_commit: COMMIT; +step s1_create_function_with_function: <... completed> +ERROR: function f() does not exist + +starting permutation: s1_begin s1_create_domain_with_domain s2_drop_domain_id s1_commit +step s1_begin: BEGIN; +step s1_create_domain_with_domain: CREATE DOMAIN idid as id; +step s2_drop_domain_id: DROP DOMAIN id; +step s1_commit: COMMIT; +step s2_drop_domain_id: <... completed> +ERROR: cannot drop type id because other objects depend on it + +starting permutation: s2_begin s2_drop_domain_id s1_create_domain_with_domain s2_commit +step s2_begin: BEGIN; +step s2_drop_domain_id: DROP DOMAIN id; +step s1_create_domain_with_domain: CREATE DOMAIN idid as id; +step s2_commit: COMMIT; +step s1_create_domain_with_domain: <... completed> +ERROR: type id does not exist + +starting permutation: s1_begin s1_create_table_with_type s2_drop_footab_type s1_commit +step s1_begin: BEGIN; +step s1_create_table_with_type: CREATE TABLE tabtype(a footab); +step s2_drop_footab_type: DROP TYPE public.footab; +step s1_commit: COMMIT; +step s2_drop_footab_type: <... completed> +ERROR: cannot drop type footab because other objects depend on it + +starting permutation: s2_begin s2_drop_footab_type s1_create_table_with_type s2_commit +step s2_begin: BEGIN; +step s2_drop_footab_type: DROP TYPE public.footab; +step s1_create_table_with_type: CREATE TABLE tabtype(a footab); +step s2_commit: COMMIT; +step s1_create_table_with_type: <... completed> +ERROR: type footab does not exist + +starting permutation: s1_begin s1_create_server_with_fdw_wrapper s2_drop_fdw_wrapper s1_commit +step s1_begin: BEGIN; +step s1_create_server_with_fdw_wrapper: CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper; +step s2_drop_fdw_wrapper: DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT; +step s1_commit: COMMIT; +step s2_drop_fdw_wrapper: <... completed> +ERROR: cannot drop foreign-data wrapper fdw_wrapper because other objects depend on it + +starting permutation: s2_begin s2_drop_fdw_wrapper s1_create_server_with_fdw_wrapper s2_commit +step s2_begin: BEGIN; +step s2_drop_fdw_wrapper: DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT; +step s1_create_server_with_fdw_wrapper: CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper; +step s2_commit: COMMIT; +step s1_create_server_with_fdw_wrapper: <... completed> +ERROR: foreign-data wrapper fdw_wrapper does not exist diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule index 0342eb39e4..1b67f0bffe 100644 --- a/src/test/isolation/isolation_schedule +++ b/src/test/isolation/isolation_schedule @@ -114,3 +114,4 @@ test: serializable-parallel-2 test: serializable-parallel-3 test: matview-write-skew test: lock-nowait +test: test_dependencies_locks diff --git a/src/test/isolation/specs/test_dependencies_locks.spec b/src/test/isolation/specs/test_dependencies_locks.spec new file mode 100644 index 0000000000..5d04dfe9dc --- /dev/null +++ b/src/test/isolation/specs/test_dependencies_locks.spec @@ -0,0 +1,89 @@ +setup +{ + CREATE SCHEMA testschema; + CREATE SCHEMA alterschema; + CREATE TYPE public.foo as enum ('one', 'two'); + CREATE TYPE public.footab as enum ('three', 'four'); + CREATE DOMAIN id AS int; + CREATE FUNCTION f() RETURNS int LANGUAGE SQL RETURN 1; + CREATE FUNCTION public.falter() RETURNS int LANGUAGE SQL RETURN 1; + CREATE FOREIGN DATA WRAPPER fdw_wrapper; +} + +teardown +{ + DROP FUNCTION IF EXISTS testschema.foo(); + DROP FUNCTION IF EXISTS fooargtype(num foo); + DROP FUNCTION IF EXISTS footrettype(); + DROP FUNCTION IF EXISTS foofunc(); + DROP FUNCTION IF EXISTS public.falter(); + DROP FUNCTION IF EXISTS alterschema.falter(); + DROP DOMAIN IF EXISTS idid; + DROP SERVER IF EXISTS srv_fdw_wrapper; + DROP TABLE IF EXISTS tabtype; + DROP SCHEMA IF EXISTS testschema; + DROP SCHEMA IF EXISTS alterschema; + DROP TYPE IF EXISTS public.foo; + DROP TYPE IF EXISTS public.footab; + DROP DOMAIN IF EXISTS id; + DROP FUNCTION IF EXISTS f(); + DROP FOREIGN DATA WRAPPER IF EXISTS fdw_wrapper; +} + +session "s1" + +step "s1_begin" { BEGIN; } +step "s1_create_function_in_schema" { CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; } +step "s1_create_function_with_argtype" { CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; } +step "s1_create_function_with_rettype" { CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1; } +step "s1_create_function_with_function" { CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1; } +step "s1_alter_function_schema" { ALTER FUNCTION public.falter() SET SCHEMA alterschema; } +step "s1_create_domain_with_domain" { CREATE DOMAIN idid as id; } +step "s1_create_table_with_type" { CREATE TABLE tabtype(a footab); } +step "s1_create_server_with_fdw_wrapper" { CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper; } +step "s1_commit" { COMMIT; } + +session "s2" + +step "s2_begin" { BEGIN; } +step "s2_drop_schema" { DROP SCHEMA testschema; } +step "s2_drop_alterschema" { DROP SCHEMA alterschema; } +step "s2_drop_foo_type" { DROP TYPE public.foo; } +step "s2_drop_foo_rettype" { DROP DOMAIN id; } +step "s2_drop_footab_type" { DROP TYPE public.footab; } +step "s2_drop_function_f" { DROP FUNCTION f(); } +step "s2_drop_domain_id" { DROP DOMAIN id; } +step "s2_drop_fdw_wrapper" { DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT; } +step "s2_commit" { COMMIT; } + +# function - schema +permutation "s1_begin" "s1_create_function_in_schema" "s2_drop_schema" "s1_commit" +permutation "s2_begin" "s2_drop_schema" "s1_create_function_in_schema" "s2_commit" + +# alter function - schema +permutation "s1_begin" "s1_alter_function_schema" "s2_drop_alterschema" "s1_commit" +permutation "s2_begin" "s2_drop_alterschema" "s1_alter_function_schema" "s2_commit" + +# function - argtype +permutation "s1_begin" "s1_create_function_with_argtype" "s2_drop_foo_type" "s1_commit" +permutation "s2_begin" "s2_drop_foo_type" "s1_create_function_with_argtype" "s2_commit" + +# function - rettype +permutation "s1_begin" "s1_create_function_with_rettype" "s2_drop_foo_rettype" "s1_commit" +permutation "s2_begin" "s2_drop_foo_rettype" "s1_create_function_with_rettype" "s2_commit" + +# function - function +permutation "s1_begin" "s1_create_function_with_function" "s2_drop_function_f" "s1_commit" +permutation "s2_begin" "s2_drop_function_f" "s1_create_function_with_function" "s2_commit" + +# domain - domain +permutation "s1_begin" "s1_create_domain_with_domain" "s2_drop_domain_id" "s1_commit" +permutation "s2_begin" "s2_drop_domain_id" "s1_create_domain_with_domain" "s2_commit" + +# table - type +permutation "s1_begin" "s1_create_table_with_type" "s2_drop_footab_type" "s1_commit" +permutation "s2_begin" "s2_drop_footab_type" "s1_create_table_with_type" "s2_commit" + +# server - foreign data wrapper +permutation "s1_begin" "s1_create_server_with_fdw_wrapper" "s2_drop_fdw_wrapper" "s1_commit" +permutation "s2_begin" "s2_drop_fdw_wrapper" "s1_create_server_with_fdw_wrapper" "s2_commit" -- 2.34.1