From: Bertrand Drouvot Date: Fri, 29 Mar 2024 15:43:26 +0000 Subject: [PATCH v9] 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 when 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 | 83 +++++++++-- src/backend/catalog/heap.c | 7 +- src/backend/catalog/index.c | 16 ++- src/backend/catalog/objectaddress.c | 57 ++++++++ src/backend/catalog/pg_aggregate.c | 2 +- src/backend/catalog/pg_attrdef.c | 1 + src/backend/catalog/pg_cast.c | 2 +- src/backend/catalog/pg_collation.c | 1 + src/backend/catalog/pg_constraint.c | 10 +- src/backend/catalog/pg_conversion.c | 2 + src/backend/catalog/pg_depend.c | 34 ++++- src/backend/catalog/pg_operator.c | 2 +- src/backend/catalog/pg_proc.c | 12 +- src/backend/catalog/pg_publication.c | 5 + src/backend/catalog/pg_range.c | 3 +- src/backend/catalog/pg_type.c | 18 ++- src/backend/catalog/toasting.c | 1 + src/backend/commands/alter.c | 3 + src/backend/commands/amcmds.c | 1 + src/backend/commands/cluster.c | 2 + src/backend/commands/event_trigger.c | 1 + src/backend/commands/extension.c | 4 +- src/backend/commands/foreigncmds.c | 7 + src/backend/commands/functioncmds.c | 3 +- src/backend/commands/indexcmds.c | 2 + src/backend/commands/opclasscmds.c | 18 +++ src/backend/commands/policy.c | 2 + src/backend/commands/proclang.c | 2 +- src/backend/commands/sequence.c | 1 + src/backend/commands/statscmds.c | 3 + src/backend/commands/tablecmds.c | 10 ++ src/backend/commands/trigger.c | 18 ++- src/backend/commands/tsearchcmds.c | 8 +- src/backend/commands/typecmds.c | 4 + src/backend/rewrite/rewriteDefine.c | 1 + src/backend/utils/errcodes.txt | 1 + src/include/catalog/dependency.h | 9 +- src/include/catalog/objectaddress.h | 1 + .../expected/test_dependencies_locks.out | 129 ++++++++++++++++++ src/test/isolation/isolation_schedule | 1 + .../specs/test_dependencies_locks.spec | 89 ++++++++++++ 42 files changed, 534 insertions(+), 43 deletions(-) 34.8% src/backend/catalog/ 16.2% src/backend/commands/ 28.0% src/test/isolation/expected/ 17.4% src/test/isolation/specs/ 3.3% src/ diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 143876b77f..6b91402a9f 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; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); } } diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index d4b5b2ade1..42293220fd 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -1517,6 +1517,68 @@ AcquireDeletionLock(const ObjectAddress *object, int flags) } } +/* + * depLockAndCheckObject + * + * 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 +depLockAndCheckObject(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 +depLockAndCheckObjects(const ObjectAddress *object, int nobject) +{ + int i; + + if (nobject < 0) + return; + + for (i = 0; i < nobject; i++, object++) + depLockAndCheckObject(object); + + return; +} + /* * ReleaseDeletionLock - release an object deletion lock * @@ -1562,13 +1624,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); } @@ -1652,9 +1709,12 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, /* Record the self-dependencies with the appropriate direction */ if (!reverse_self) + { + depLockAndCheckObjects(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 */ @@ -1664,6 +1724,7 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, { ObjectAddress *thisobj = self_addrs->refs + selfref; + depLockAndCheckObject(depender); recordDependencyOn(thisobj, depender, self_behavior); } } @@ -1672,6 +1733,7 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, } /* Record the external dependencies */ + depLockAndCheckObjects(context.addrs->refs, context.addrs->numrefs); recordMultipleDependencies(depender, context.addrs->refs, context.addrs->numrefs, behavior); @@ -2737,11 +2799,12 @@ stack_address_present_add_flags(const ObjectAddress *object, * removing any duplicates. */ void -record_object_address_dependencies(const ObjectAddress *depender, - ObjectAddresses *referenced, - DependencyType behavior) +lock_record_object_address_dependencies(const ObjectAddress *depender, + ObjectAddresses *referenced, + DependencyType behavior) { eliminate_duplicate_dependencies(referenced); + depLockAndCheckObjects(referenced->refs, referenced->numrefs); recordMultipleDependencies(depender, referenced->refs, referenced->numrefs, behavior); diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index a122bbffce..113b275fe1 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); + depLockAndCheckObject(&referenced); 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); + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } } @@ -1471,7 +1473,7 @@ heap_create_with_catalog(const char *relname, add_exact_object_address(&referenced, addrs); } - record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); } @@ -3393,7 +3395,7 @@ StorePartitionKey(Relation rel, } } - record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); /* @@ -3409,6 +3411,7 @@ StorePartitionKey(Relation rel, ObjectAddressSubSet(referenced, RelationRelationId, RelationGetRelid(rel), partattrs[i]); + depLockAndCheckObject(&myself); recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 55fdde4b24..26201e7610 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1144,7 +1144,7 @@ index_create(Relation heapRelation, add_exact_object_address(&referenced, addrs); } - record_object_address_dependencies(&myself, addrs, DEPENDENCY_AUTO); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_AUTO); free_object_addresses(addrs); } @@ -1157,9 +1157,11 @@ index_create(Relation heapRelation, if (OidIsValid(parentIndexRelid)) { ObjectAddressSet(referenced, RelationRelationId, parentIndexRelid); + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, heapRelationId); + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC); } @@ -1185,7 +1187,7 @@ index_create(Relation heapRelation, add_exact_object_address(&referenced, addrs); } - record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); /* Store dependencies on anything mentioned in index expressions */ @@ -1987,6 +1989,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(); + depLockAndCheckObject(&myself); recordDependencyOn(&idxaddr, &myself, DEPENDENCY_INTERNAL); /* @@ -1998,9 +2008,11 @@ index_constraint_create(Relation heapRelation, ObjectAddress referenced; ObjectAddressSet(referenced, ConstraintRelationId, parentConstraintId); + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(heapRelation)); + depLockAndCheckObject(&referenced); 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..9b5c8c8fb0 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -805,7 +805,7 @@ AggregateCreate(const char *aggName, add_exact_object_address(&referenced, addrs); } - record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); return myself; } diff --git a/src/backend/catalog/pg_attrdef.c b/src/backend/catalog/pg_attrdef.c index 003ae70b4d..47b7fe75ad 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; + depLockAndCheckObject(&colobject); 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..2593bb3634 100644 --- a/src/backend/catalog/pg_cast.c +++ b/src/backend/catalog/pg_cast.c @@ -121,7 +121,7 @@ CastCreate(Oid sourcetypeid, Oid targettypeid, add_exact_object_address(&referenced, addrs); } - record_object_address_dependencies(&myself, addrs, behavior); + lock_record_object_address_dependencies(&myself, addrs, behavior); free_object_addresses(addrs); /* dependency on extension */ diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c index 7f2f701229..43ceb426d4 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; + depLockAndCheckObject(&referenced); 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..91af823ee5 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -277,8 +277,8 @@ CreateConstraintEntry(const char *constraintName, add_exact_object_address(&domobject, addrs_auto); } - record_object_address_dependencies(&conobject, addrs_auto, - DEPENDENCY_AUTO); + lock_record_object_address_dependencies(&conobject, addrs_auto, + DEPENDENCY_AUTO); free_object_addresses(addrs_auto); /* Handle set of normal dependencies */ @@ -352,8 +352,8 @@ CreateConstraintEntry(const char *constraintName, } } - record_object_address_dependencies(&conobject, addrs_normal, - DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&conobject, addrs_normal, + DEPENDENCY_NORMAL); free_object_addresses(addrs_normal); /* @@ -858,9 +858,11 @@ ConstraintSetParentConstraint(Oid childConstrId, ObjectAddressSet(depender, ConstraintRelationId, childConstrId); ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId); + depLockAndCheckObject(&referenced); recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, childTableId); + depLockAndCheckObject(&referenced); 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..479dd528eb 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; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* create dependency on namespace */ referenced.classId = NamespaceRelationId; referenced.objectId = connamespace; referenced.objectSubId = 0; + depLockAndCheckObject(&referenced); 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..47968ee8c8 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 + * depLockAndCheckObject()) when recording the dependency. + */ + 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; + depLockAndCheckObject(&extension); recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION); } } @@ -501,11 +520,18 @@ changeDependencyFor(Oid classId, Oid objectId, depAddr.classId = classId; depAddr.objectId = objectId; depAddr.objectSubId = 0; + depLockAndCheckObject(&objAddr); recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL); return 1; } + /* + * Acquire a lock and check object still exists while changing the + * dependency. + */ + depLockAndCheckObject(&objAddr); + depRel = table_open(DependRelationId, RowExclusiveLock); /* There should be existing dependency record(s), so search. */ @@ -706,7 +732,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..7e40471baf 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -930,7 +930,7 @@ makeOperatorDependencies(HeapTuple tuple, add_exact_object_address(&referenced, addrs); } - record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); /* Dependency on owner */ diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 528c17cd7f..aa36a07648 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); @@ -637,7 +644,7 @@ ProcedureCreate(const char *procedureName, add_exact_object_address(&referenced, addrs); } - record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); /* dependency on SQL routine body */ @@ -674,9 +681,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..6654b65f41 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); + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); /* Add dependency on the relation */ ObjectAddressSet(referenced, RelationRelationId, relid); + depLockAndCheckObject(&referenced); 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]); + depLockAndCheckObject(&referenced); 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); + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); /* Add dependency on the schema */ ObjectAddressSet(referenced, NamespaceRelationId, schemaid); + depLockAndCheckObject(&referenced); 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..a2f343a81a 100644 --- a/src/backend/catalog/pg_range.c +++ b/src/backend/catalog/pg_range.c @@ -92,13 +92,14 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation, add_exact_object_address(&referenced, addrs); } - record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); /* record multirange type's dependency on the range type */ referencing.classId = TypeRelationId; referencing.objectId = multirangeTypeOid; referencing.objectSubId = 0; + depLockAndCheckObject(&myself); 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..91139e275f 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -494,6 +494,14 @@ 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(); + GenerateTypeDependencies(tup, pg_type_desc, (defaultTypeBin ? @@ -505,6 +513,7 @@ TypeCreate(Oid newTypeOid, isDependentType, true, /* make extension dependency */ rebuildDeps); + } /* Post creation hook for new type */ InvokeObjectPostCreateHook(TypeRelationId, typeObjectId, 0); @@ -703,7 +712,7 @@ GenerateTypeDependencies(HeapTuple typeTuple, add_exact_object_address(&referenced, addrs_normal); } - record_object_address_dependencies(&myself, addrs_normal, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs_normal, DEPENDENCY_NORMAL); free_object_addresses(addrs_normal); /* Normal dependency on the default expression. */ @@ -724,9 +733,15 @@ GenerateTypeDependencies(HeapTuple typeTuple, ObjectAddressSet(referenced, RelationRelationId, typeForm->typrelid); if (relationKind != RELKIND_COMPOSITE_TYPE) + { + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); + } else + { + depLockAndCheckObject(&myself); recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); + } } /* @@ -737,6 +752,7 @@ GenerateTypeDependencies(HeapTuple typeTuple, if (OidIsValid(typeForm->typelem)) { ObjectAddressSet(referenced, TypeRelationId, typeForm->typelem); + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, isImplicitArray ? DEPENDENCY_INTERNAL : DEPENDENCY_NORMAL); } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 738bc46ae8..a471833efe 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; + depLockAndCheckObject(&baseobject); recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 4f99ebb447..e3ad5db529 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)) + { + depLockAndCheckObject(&refAddr); recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION); + } } return address; diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c index aaa0f9a1dc..0b4e44131a 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; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOnCurrentExtension(&myself, false); diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 78f96789b0..73bcd68a1f 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -1381,6 +1381,7 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, { baseobject.objectId = r1; toastobject.objectId = relform1->reltoastrelid; + depLockAndCheckObject(&baseobject); recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } @@ -1389,6 +1390,7 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, { baseobject.objectId = r2; toastobject.objectId = relform2->reltoastrelid; + depLockAndCheckObject(&baseobject); recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 7a5ed6b985..ddcba91c92 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; + depLockAndCheckObject(&referenced); 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..fdcffcea01 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -1935,7 +1935,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner, } /* Record all of them (this includes duplicate elimination) */ - record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL); free_object_addresses(refobjs); /* Post creation hook for new extension */ @@ -3258,6 +3258,7 @@ ApplyExtensionUpdates(Oid extensionOid, otherext.objectId = reqext; otherext.objectSubId = 0; + depLockAndCheckObject(&otherext); recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL); } @@ -3414,6 +3415,7 @@ ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt, /* * OK, add the dependency. */ + depLockAndCheckObject(&extension); recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION); /* diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index cf61bbac1f..5e98f3d0d6 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; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } @@ -650,6 +651,7 @@ CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt) referenced.classId = ProcedureRelationId; referenced.objectId = fdwvalidator; referenced.objectSubId = 0; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } @@ -811,6 +813,7 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt) referenced.classId = ProcedureRelationId; referenced.objectId = fdwhandler; referenced.objectSubId = 0; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } @@ -819,6 +822,7 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt) referenced.classId = ProcedureRelationId; referenced.objectId = fdwvalidator; referenced.objectSubId = 0; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } } @@ -951,6 +955,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt) referenced.classId = ForeignDataWrapperRelationId; referenced.objectId = fdw->fdwid; referenced.objectSubId = 0; + depLockAndCheckObject(&referenced); 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; + depLockAndCheckObject(&referenced); 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; + depLockAndCheckObject(&referenced); 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..2a378a53d8 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -1459,6 +1459,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt) referenced.classId = ProcedureRelationId; referenced.objectId = newsupport; referenced.objectSubId = 0; + depLockAndCheckObject(&referenced); recordDependencyOn(&address, &referenced, DEPENDENCY_NORMAL); } @@ -1979,7 +1980,7 @@ CreateTransform(CreateTransformStmt *stmt) add_exact_object_address(&referenced, addrs); } - record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); /* dependency on extension */ diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 309389e20d..487708d885 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); + depLockAndCheckObject(&parentIdx); recordDependencyOn(&partIdx, &parentIdx, DEPENDENCY_PARTITION_PRI); + depLockAndCheckObject(&partitionTbl); recordDependencyOn(&partIdx, &partitionTbl, DEPENDENCY_PARTITION_SEC); } diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index b8b5c147c5..e7b1cb911e 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; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); /* dependency on namespace */ referenced.classId = NamespaceRelationId; referenced.objectId = namespaceoid; referenced.objectSubId = 0; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on owner */ @@ -725,18 +727,21 @@ DefineOpClass(CreateOpClassStmt *stmt) referenced.classId = NamespaceRelationId; referenced.objectId = namespaceoid; referenced.objectSubId = 0; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on opfamily */ referenced.classId = OperatorFamilyRelationId; referenced.objectId = opfamilyoid; referenced.objectSubId = 0; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); /* dependency on indexed datatype */ referenced.classId = TypeRelationId; referenced.objectId = typeoid; referenced.objectSubId = 0; + depLockAndCheckObject(&referenced); 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; + depLockAndCheckObject(&referenced); 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 */ + depLockAndCheckObject(&referenced); 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; + depLockAndCheckObject(&referenced); 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; + depLockAndCheckObject(&referenced); 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 */ + depLockAndCheckObject(&referenced); 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; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, proc->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO); diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c index 6ff3eba824..5a50ad73fb 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; + depLockAndCheckObject(&target); 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; + depLockAndCheckObject(&target); 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..679155162e 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -205,7 +205,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) add_exact_object_address(&referenced, addrs); } - record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); /* Post creation hook for new procedural language */ diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 28f8522264..0d37576d22 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; + depLockAndCheckObject(&refobject); recordDependencyOn(&depobject, &refobject, deptype); } diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c index 1db3ef69d2..455ec046f8 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]); + depLockAndCheckObject(&parentobject); recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO); } @@ -553,6 +554,7 @@ CreateStatistics(CreateStatsStmt *stmt) if (!nattnums) { ObjectAddressSet(parentobject, RelationRelationId, relid); + depLockAndCheckObject(&parentobject); recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO); } @@ -573,6 +575,7 @@ CreateStatistics(CreateStatsStmt *stmt) * than the underlying table(s). */ ObjectAddressSet(parentobject, NamespaceRelationId, namespaceId); + depLockAndCheckObject(&parentobject); recordDependencyOn(&myself, &parentobject, DEPENDENCY_NORMAL); recordDependencyOnOwner(StatisticExtRelationId, statoid, stxowner); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 7b6c69b7a5..73696a4210 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -3437,6 +3437,7 @@ StoreCatalogInheritance1(Oid relationId, Oid parentOid, childobject.objectId = relationId; childobject.objectSubId = 0; + depLockAndCheckObject(&parentobject); recordDependencyOn(&childobject, &parentobject, child_dependency_type(child_is_partition)); @@ -7440,6 +7441,7 @@ add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid) referenced.classId = TypeRelationId; referenced.objectId = typid; referenced.objectSubId = 0; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } @@ -7461,6 +7463,7 @@ add_column_collation_dependency(Oid relid, int32 attnum, Oid collid) referenced.classId = CollationRelationId; referenced.objectId = collid; referenced.objectSubId = 0; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } } @@ -10140,6 +10143,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, ObjectAddress referenced; ObjectAddressSet(referenced, ConstraintRelationId, parentConstr); + depLockAndCheckObject(&referenced); recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL); } @@ -10431,8 +10435,10 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, */ ObjectAddressSet(address, ConstraintRelationId, constrOid); ObjectAddressSet(referenced, ConstraintRelationId, parentConstr); + depLockAndCheckObject(&referenced); recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, partitionId); + depLockAndCheckObject(&referenced); recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC); /* Make all this visible before recursing */ @@ -10933,9 +10939,11 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) /* Set up partition dependencies for the new constraint */ ObjectAddressSet(address, ConstraintRelationId, constrOid); ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid); + depLockAndCheckObject(&referenced); recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(partRel)); + depLockAndCheckObject(&referenced); recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC); /* Done with the cloned constraint's tuple */ @@ -14782,6 +14790,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId) */ ObjectAddressSet(relobj, RelationRelationId, reloid); ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam); + depLockAndCheckObject(&referenced); recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL); } else if (OidIsValid(oldAccessMethodId) && @@ -16400,6 +16409,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode) typeobj.classId = TypeRelationId; typeobj.objectId = typeid; typeobj.objectSubId = 0; + depLockAndCheckObject(&typeobj); recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL); /* Update pg_class.reloftype */ diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 95de402fa6..c0e7ddff1c 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; + depLockAndCheckObject(&referenced); 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; + depLockAndCheckObject(&referenced); 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; + depLockAndCheckObject(&referenced); 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; + depLockAndCheckObject(&referenced); 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; + depLockAndCheckObject(&myself); recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); } @@ -1100,8 +1109,10 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, if (OidIsValid(parentTriggerOid)) { ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid); + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel)); + depLockAndCheckObject(&referenced); 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]; + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } } @@ -1255,9 +1267,11 @@ TriggerSetParentTrigger(Relation trigRel, ObjectAddressSet(depender, TriggerRelationId, childTrigId); ObjectAddressSet(referenced, TriggerRelationId, parentTrigId); + depLockAndCheckObject(&referenced); recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI); ObjectAddressSet(referenced, RelationRelationId, childTableId); + depLockAndCheckObject(&referenced); recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC); } else diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c index b7b5019f1e..a62c997725 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -171,7 +171,7 @@ makeParserDependencies(HeapTuple tuple) add_exact_object_address(&referenced, addrs); } - record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); return myself; @@ -329,7 +329,7 @@ makeDictionaryDependencies(HeapTuple tuple) ObjectAddressSet(referenced, TSTemplateRelationId, dict->dicttemplate); add_exact_object_address(&referenced, addrs); - record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); return myself; @@ -677,7 +677,7 @@ makeTSTemplateDependencies(HeapTuple tuple) add_exact_object_address(&referenced, addrs); } - record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); return myself; @@ -885,7 +885,7 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld, } /* Record 'em (this includes duplicate elimination) */ - record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 2a1e713335..cbb61c1207 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. */ + depLockAndCheckObject(&referenced); 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. */ + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); pfree(argtypes); @@ -1898,6 +1900,7 @@ makeMultirangeConstructors(const char *name, Oid namespace, 1.0, /* procost */ 0.0); /* prorows */ /* ditto */ + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); pfree(argtypes); *castFuncOid = myself.objectId; @@ -1936,6 +1939,7 @@ makeMultirangeConstructors(const char *name, Oid namespace, 1.0, /* procost */ 0.0); /* prorows */ /* ditto */ + depLockAndCheckObject(&referenced); recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); pfree(argtypes); pfree(allParameterTypes); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 6cc9a8d8bf..53308dceb6 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; + depLockAndCheckObject(&referenced); 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..f404a92fb4 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -101,6 +101,8 @@ typedef struct ObjectAddresses ObjectAddresses; /* in dependency.c */ extern void AcquireDeletionLock(const ObjectAddress *object, int flags); +extern void depLockAndCheckObject(const ObjectAddress *object); +extern void depLockAndCheckObjects(const ObjectAddress *object, int nobject); extern void ReleaseDeletionLock(const ObjectAddress *object); @@ -128,9 +130,9 @@ extern void add_exact_object_address(const ObjectAddress *object, extern bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs); -extern void record_object_address_dependencies(const ObjectAddress *depender, - ObjectAddresses *referenced, - DependencyType behavior); +extern void lock_record_object_address_dependencies(const ObjectAddress *depender, + ObjectAddresses *referenced, + DependencyType behavior); extern void sort_object_addresses(ObjectAddresses *addrs); @@ -172,6 +174,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 --xlZXO4tPcOr9Z9/W--