]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Avoid duplicates in ALTER ... DEPENDS ON EXTENSION
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 11 Mar 2020 14:04:59 +0000 (11:04 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 11 Mar 2020 14:04:59 +0000 (11:04 -0300)
If the command is attempted for an extension that the object already
depends on, silently do nothing.

In particular, this means that if a database containing multiple such
entries is dumped, the restore will silently do the right thing and
record just the first one.  (At least, in a world where pg_dump does
dump such entries -- which it doesn't currently, but it will.)

Backpatch to 9.6, where this kind of dependency was introduced.

Reviewed-by: Ibrar Ahmed, Tom Lane (offlist)
Discussion: https://postgr.es/m/20200217225333.GA30974@alvherre.pgsql

src/backend/catalog/pg_depend.c
src/backend/commands/alter.c
src/include/catalog/dependency.h
src/test/modules/test_extensions/expected/test_extdepend.out
src/test/modules/test_extensions/sql/test_extdepend.sql

index 4116e93b64ba2ecdc8204ae0e61b80d8a5469937..9ffadbbc18177e2439f49136a5966de13edbb35c 100644 (file)
@@ -648,6 +648,49 @@ getExtensionOfObject(Oid classId, Oid objectId)
        return result;
 }
 
+/*
+ * Return (possibly NIL) list of extensions that the given object depends on
+ * in DEPENDENCY_AUTO_EXTENSION mode.
+ */
+List *
+getAutoExtensionsOfObject(Oid classId, Oid objectId)
+{
+       List       *result = NIL;
+       Relation        depRel;
+       ScanKeyData     key[2];
+       SysScanDesc     scan;
+       HeapTuple       tup;
+
+       depRel = table_open(DependRelationId, AccessShareLock);
+
+       ScanKeyInit(&key[0],
+                               Anum_pg_depend_classid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(classId));
+       ScanKeyInit(&key[1],
+                               Anum_pg_depend_objid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(objectId));
+
+       scan = systable_beginscan(depRel, DependDependerIndexId, true,
+                                                         NULL, 2, key);
+
+       while (HeapTupleIsValid((tup = systable_getnext(scan))))
+       {
+               Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
+
+               if (depform->refclassid == ExtensionRelationId &&
+                       depform->deptype == DEPENDENCY_AUTO_EXTENSION)
+                       result = lappend_oid(result, depform->refobjid);
+       }
+
+       systable_endscan(scan);
+
+       table_close(depRel, AccessShareLock);
+
+       return result;
+}
+
 /*
  * Detect whether a sequence is marked as "owned" by a column
  *
index 40ca64e22617cd6d109c57cd21182441fd722fb2..c8f471be38023cb4c748bfa973edfce27f920f4f 100644 (file)
@@ -434,6 +434,7 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre
        ObjectAddress address;
        ObjectAddress refAddr;
        Relation        rel;
+       List   *currexts;
 
        address =
                get_object_address_rv(stmt->objectType, stmt->relation, (List *) stmt->object,
@@ -463,7 +464,11 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre
        if (refAddress)
                *refAddress = refAddr;
 
-       recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
+       /* Avoid duplicates */
+       currexts = getAutoExtensionsOfObject(address.classId,
+                                                                                address.objectId);
+       if (!list_member_oid(currexts, refAddr.objectId))
+               recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
 
        return address;
 }
index bee9b3ff57f0df53fdffb7ca039ab2f862ca996d..9a11acfadea33c11eba1a8337992c3d73c3c2904 100644 (file)
@@ -207,6 +207,7 @@ extern long changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
                                                                 Oid newRefObjectId);
 
 extern Oid     getExtensionOfObject(Oid classId, Oid objectId);
+extern List *getAutoExtensionsOfObject(Oid classId, Oid objectId);
 
 extern bool sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId);
 extern List *getOwnedSequences(Oid relid, AttrNumber attnum);
index 11e441ddd37794acfa07cb6c35c9b929a2bcb938..40533e90de30e0ece611f297319b3f345fff98c7 100644 (file)
@@ -47,6 +47,8 @@ SELECT * FROM test_extdep_commands \gexec
  CREATE INDEX e ON a (a1)
  ALTER INDEX e DEPENDS ON EXTENSION test_ext5
  RESET search_path
+-- A dependent object made dependent again has no effect
+ALTER FUNCTION test_ext.b() DEPENDS ON EXTENSION test_ext5;
 -- make sure we have the right dependencies on the extension
 SELECT deptype, p.*
   FROM pg_depend, pg_identify_object(classid, objid, objsubid) AS p
index cf44145dcb343b35515d7accde3e98134f9c0d8e..cc170ab70975c5b68ce0f8ef0f56417cfe7d730c 100644 (file)
@@ -27,6 +27,8 @@ COPY test_extdep_commands FROM stdin;
 SELECT * FROM test_extdep_commands;
 -- First, test that dependent objects go away when the extension is dropped.
 SELECT * FROM test_extdep_commands \gexec
+-- A dependent object made dependent again has no effect
+ALTER FUNCTION test_ext.b() DEPENDS ON EXTENSION test_ext5;
 -- make sure we have the right dependencies on the extension
 SELECT deptype, p.*
   FROM pg_depend, pg_identify_object(classid, objid, objsubid) AS p