Add ALTER .. NO DEPENDS ON
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 20 Apr 2020 17:42:12 +0000 (13:42 -0400)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 20 Apr 2020 17:42:12 +0000 (13:42 -0400)
Commit f2fcad27d59c (9.6 era) added the ability to mark objects as
dependent an extension, but forgot to add a way for such dependencies to
be removed.  This commit fixes that oversight.

Strictly speaking this should be backpatched to 9.6, but due to lack of
demand we're not doing so at this time.

Discussion: https://postgr.es/m/20200217225333.GA30974@alvherre.pgsql
Reviewed-by: ahsan hadi <ahsan.hadi@gmail.com>
Reviewed-by: Ibrar Ahmed <ibrar.ahmad@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
13 files changed:
doc/src/sgml/ref/alter_function.sgml
doc/src/sgml/ref/alter_index.sgml
doc/src/sgml/ref/alter_materialized_view.sgml
doc/src/sgml/ref/alter_trigger.sgml
src/backend/catalog/pg_depend.c
src/backend/commands/alter.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/include/catalog/dependency.h
src/include/nodes/parsenodes.h
src/test/modules/test_extensions/expected/test_extdepend.out
src/test/modules/test_extensions/sql/test_extdepend.sql

index 03ffa5945a2a6a3917a8bcb14544a9cb5908a5ec..70b1f24bc003a7459cc86ac04e92d99c51d3feaf 100644 (file)
@@ -30,7 +30,7 @@ ALTER FUNCTION <replaceable>name</replaceable> [ ( [ [ <replaceable class="param
 ALTER FUNCTION <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
     SET SCHEMA <replaceable>new_schema</replaceable>
 ALTER FUNCTION <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
-    DEPENDS ON EXTENSION <replaceable>extension_name</replaceable>
+    [ NO ] DEPENDS ON EXTENSION <replaceable>extension_name</replaceable>
 
 <phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase>
 
@@ -153,10 +153,14 @@ ALTER FUNCTION <replaceable>name</replaceable> [ ( [ [ <replaceable class="param
    </varlistentry>
 
    <varlistentry>
-    <term><replaceable class="parameter">extension_name</replaceable></term>
+    <term><literal>DEPENDS ON EXTENSION <replaceable class="parameter">extension_name</replaceable></literal></term>
+    <term><literal>NO DEPENDS ON EXTENSION <replaceable class="parameter">extension_name</replaceable></literal></term>
     <listitem>
      <para>
-      The name of the extension that the function is to depend on.
+      This form marks the function as dependent on the extension, or no longer
+      dependent on that extension if <literal>NO</literal> is specified.
+      A function that's marked as dependent on an extension is automatically
+      dropped when the extension is dropped.
      </para>
     </listitem>
    </varlistentry>
index 6d34dbb74e5b860b63705144e6ae975ea58c184e..de6f89d458cb2f79b7dc45dead35dc4bb30c13a0 100644 (file)
@@ -100,11 +100,14 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="parameter">name</replaceable>
    </varlistentry>
 
    <varlistentry>
-    <term><literal>DEPENDS ON EXTENSION</literal></term>
+    <term><literal>DEPENDS ON EXTENSION <replaceable class="parameter">extension_name</replaceable></literal></term>
+    <term><literal>NO DEPENDS ON EXTENSION <replaceable class="parameter">extension_name</replaceable></literal></term>
     <listitem>
      <para>
-      This form marks the index as dependent on the extension, such that if the
-      extension is dropped, the index will automatically be dropped as well.
+      This form marks the index as dependent on the extension, or no longer
+      dependent on that extension if <literal>NO</literal> is specified.
+      An index that's marked as dependent on an extension is automatically
+      dropped when the extension is dropped.
      </para>
     </listitem>
    </varlistentry>
index 03e3df1ffdbb353aa223fe40fe079dd7f6ea6910..9df8a79977169284217b152da5ca0f1caf352884 100644 (file)
@@ -68,12 +68,6 @@ ALTER MATERIALIZED VIEW ALL IN TABLESPACE <replaceable class="parameter">name</r
    anyway.)
   </para>
 
-  <para>
-   The <literal>DEPENDS ON EXTENSION</literal> form marks the materialized view
-   as dependent on an extension, such that the materialized view will
-   automatically be dropped if the extension is dropped.
-  </para>
-
   <para>
    The statement subforms and actions available for
    <command>ALTER MATERIALIZED VIEW</command> are a subset of those available
@@ -110,7 +104,10 @@ ALTER MATERIALIZED VIEW ALL IN TABLESPACE <replaceable class="parameter">name</r
      <term><replaceable class="parameter">extension_name</replaceable></term>
      <listitem>
       <para>
-       The name of the extension that the materialized view is to depend on.
+       The name of the extension that the materialized view is to depend on (or no longer
+       dependent on, if <literal>NO</literal> is specified).  A materialized view
+       that's marked as dependent on an extension is automatically dropped when
+       the extension is dropped.
       </para>
      </listitem>
     </varlistentry>
index 6cf789a67a3bc94a123649c4d31c0a978841ed78..6d4784c82f195986d2a470079ef170789e3e456d 100644 (file)
@@ -22,7 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 ALTER TRIGGER <replaceable class="parameter">name</replaceable> ON <replaceable class="parameter">table_name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
-ALTER TRIGGER <replaceable class="parameter">name</replaceable> ON <replaceable class="parameter">table_name</replaceable> DEPENDS ON EXTENSION <replaceable class="parameter">extension_name</replaceable>
+ALTER TRIGGER <replaceable class="parameter">name</replaceable> ON <replaceable class="parameter">table_name</replaceable> [ NO ] DEPENDS ON EXTENSION <replaceable class="parameter">extension_name</replaceable>
 </synopsis>
  </refsynopsisdiv>
 
@@ -78,7 +78,10 @@ ALTER TRIGGER <replaceable class="parameter">name</replaceable> ON <replaceable
     <term><replaceable class="parameter">extension_name</replaceable></term>
     <listitem>
      <para>
-      The name of the extension that the trigger is to depend on.
+      The name of the extension that the trigger is to depend on (or no longer
+      dependent on, if <literal>NO</literal> is specified).  A trigger
+      that's marked as dependent on an extension is automatically dropped when
+      the extension is dropped.
      </para>
     </listitem>
    </varlistentry>
index 596dafe19c8843f5d91b5c3997bab9f22d267c3b..fa38ee9477779aefcfb79c2fc6b037aa6a3ab5ae 100644 (file)
@@ -278,6 +278,55 @@ deleteDependencyRecordsForClass(Oid classId, Oid objectId,
    return count;
 }
 
+/*
+ * deleteDependencyRecordsForSpecific -- delete all records with given depender
+ * classId/objectId, dependee classId/objectId, of the given deptype.
+ * Returns the number of records deleted.
+ */
+long
+deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype,
+                                  Oid refclassId, Oid refobjectId)
+{
+   long        count = 0;
+   Relation    depRel;
+   ScanKeyData key[2];
+   SysScanDesc scan;
+   HeapTuple   tup;
+
+   depRel = table_open(DependRelationId, RowExclusiveLock);
+
+   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 == refclassId &&
+           depform->refobjid == refobjectId &&
+           depform->deptype == deptype)
+       {
+           CatalogTupleDelete(depRel, &tup->t_self);
+           count++;
+       }
+   }
+
+   systable_endscan(scan);
+
+   table_close(depRel, RowExclusiveLock);
+
+   return count;
+}
+
 /*
  * Adjust dependency record(s) to point to a different object of the same type
  *
index 11db9bfe92288f5fb4ceb16e78c39d5295548d8f..951690b2b8ddca42df7af70a0d6a769e8daa301c 100644 (file)
@@ -421,7 +421,7 @@ ExecRenameStmt(RenameStmt *stmt)
 }
 
 /*
- * Executes an ALTER OBJECT / DEPENDS ON [EXTENSION] statement.
+ * Executes an ALTER OBJECT / [NO] DEPENDS ON EXTENSION statement.
  *
  * Return value is the address of the altered object.  refAddress is an output
  * argument which, if not null, receives the address of the object that the
@@ -433,7 +433,6 @@ 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,11 +462,22 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre
    if (refAddress)
        *refAddress = refAddr;
 
-   /* Avoid duplicates */
-   currexts = getAutoExtensionsOfObject(address.classId,
-                                        address.objectId);
-   if (!list_member_oid(currexts, refAddr.objectId))
-       recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
+   if (stmt->remove)
+   {
+       deleteDependencyRecordsForSpecific(address.classId, address.objectId,
+                                          DEPENDENCY_AUTO_EXTENSION,
+                                          refAddr.classId, refAddr.objectId);
+   }
+   else
+   {
+       List   *currexts;
+
+       /* Avoid duplicates */
+       currexts = getAutoExtensionsOfObject(address.classId,
+                                            address.objectId);
+       if (!list_member_oid(currexts, refAddr.objectId))
+           recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
+   }
 
    return address;
 }
index 1525c0de7258e31583aedb9a230d48273698928d..491452ae2d41053978e77cc92c2c65d8a885f14e 100644 (file)
@@ -3638,6 +3638,7 @@ _copyAlterObjectDependsStmt(const AlterObjectDependsStmt *from)
    COPY_NODE_FIELD(relation);
    COPY_NODE_FIELD(object);
    COPY_NODE_FIELD(extname);
+   COPY_SCALAR_FIELD(remove);
 
    return newnode;
 }
index 4f34189ab5cbf36c0da6240ddfa0305f99726686..8408c28ec6990fdaaf716b2fb10d8191b014b713 100644 (file)
@@ -1449,6 +1449,7 @@ _equalAlterObjectDependsStmt(const AlterObjectDependsStmt *a, const AlterObjectD
    COMPARE_NODE_FIELD(relation);
    COMPARE_NODE_FIELD(object);
    COMPARE_NODE_FIELD(extname);
+   COMPARE_SCALAR_FIELD(remove);
 
    return true;
 }
index 1219ac8c26494df560e7ba47217073bbb069bf1e..3c78f2d1b51f2f17c9b2c24f42ce4e1a492a4ab5 100644 (file)
@@ -320,7 +320,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <list>   vac_analyze_option_list
 %type <node>   vac_analyze_option_arg
 %type <defelt> drop_option
-%type <boolean>    opt_or_replace
+%type <boolean>    opt_or_replace opt_no
                opt_grant_grant_option opt_grant_admin_option
                opt_nowait opt_if_exists opt_with_data
                opt_transaction_chain
@@ -9053,57 +9053,67 @@ opt_set_data: SET DATA_P                            { $$ = 1; }
  *****************************************************************************/
 
 AlterObjectDependsStmt:
-           ALTER FUNCTION function_with_argtypes DEPENDS ON EXTENSION name
+           ALTER FUNCTION function_with_argtypes opt_no DEPENDS ON EXTENSION name
                {
                    AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
                    n->objectType = OBJECT_FUNCTION;
                    n->object = (Node *) $3;
-                   n->extname = makeString($7);
+                   n->extname = makeString($8);
+                   n->remove = $4;
                    $$ = (Node *)n;
                }
-           | ALTER PROCEDURE function_with_argtypes DEPENDS ON EXTENSION name
+           | ALTER PROCEDURE function_with_argtypes opt_no DEPENDS ON EXTENSION name
                {
                    AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
                    n->objectType = OBJECT_PROCEDURE;
                    n->object = (Node *) $3;
-                   n->extname = makeString($7);
+                   n->extname = makeString($8);
+                   n->remove = $4;
                    $$ = (Node *)n;
                }
-           | ALTER ROUTINE function_with_argtypes DEPENDS ON EXTENSION name
+           | ALTER ROUTINE function_with_argtypes opt_no DEPENDS ON EXTENSION name
                {
                    AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
                    n->objectType = OBJECT_ROUTINE;
                    n->object = (Node *) $3;
-                   n->extname = makeString($7);
+                   n->extname = makeString($8);
+                   n->remove = $4;
                    $$ = (Node *)n;
                }
-           | ALTER TRIGGER name ON qualified_name DEPENDS ON EXTENSION name
+           | ALTER TRIGGER name ON qualified_name opt_no DEPENDS ON EXTENSION name
                {
                    AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
                    n->objectType = OBJECT_TRIGGER;
                    n->relation = $5;
                    n->object = (Node *) list_make1(makeString($3));
-                   n->extname = makeString($9);
+                   n->extname = makeString($10);
+                   n->remove = $6;
                    $$ = (Node *)n;
                }
-           | ALTER MATERIALIZED VIEW qualified_name DEPENDS ON EXTENSION name
+           | ALTER MATERIALIZED VIEW qualified_name opt_no DEPENDS ON EXTENSION name
                {
                    AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
                    n->objectType = OBJECT_MATVIEW;
                    n->relation = $4;
-                   n->extname = makeString($8);
+                   n->extname = makeString($9);
+                   n->remove = $5;
                    $$ = (Node *)n;
                }
-           | ALTER INDEX qualified_name DEPENDS ON EXTENSION name
+           | ALTER INDEX qualified_name opt_no DEPENDS ON EXTENSION name
                {
                    AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
                    n->objectType = OBJECT_INDEX;
                    n->relation = $3;
-                   n->extname = makeString($7);
+                   n->extname = makeString($8);
+                   n->remove = $4;
                    $$ = (Node *)n;
                }
        ;
 
+opt_no:        NO              { $$ = true; }
+           | /* EMPTY */   { $$ = false;   }
+       ;
+
 /*****************************************************************************
  *
  * ALTER THING name SET SCHEMA name
index ab5e92bdc6f3766da95797c3adea2a400423e8b3..2c6abe26a5ab9f925f2af7b83ca2ba0fd4f0841a 100644 (file)
@@ -196,6 +196,10 @@ extern long deleteDependencyRecordsFor(Oid classId, Oid objectId,
 extern long deleteDependencyRecordsForClass(Oid classId, Oid objectId,
                                            Oid refclassId, char deptype);
 
+extern long deleteDependencyRecordsForSpecific(Oid classId, Oid objectId,
+                                              char deptype,
+                                              Oid refclassId, Oid refobjectId);
+
 extern long changeDependencyFor(Oid classId, Oid objectId,
                                Oid refClassId, Oid oldRefObjectId,
                                Oid newRefObjectId);
index 518abe42c1098ef86c6bd620ab129335f402718c..5e1ffafb91b17e3a3de94eebf3b0e4a5725ae45f 100644 (file)
@@ -2936,6 +2936,7 @@ typedef struct AlterObjectDependsStmt
    RangeVar   *relation;       /* in case a table is involved */
    Node       *object;         /* name of the object */
    Value      *extname;        /* extension name */
+   bool        remove;         /* set true to remove dep rather than add */
 } AlterObjectDependsStmt;
 
 /* ----------------------
index 40533e90de30e0ece611f297319b3f345fff98c7..0b62015d18c80804da9e38d5232134e640601ebf 100644 (file)
@@ -150,5 +150,39 @@ SELECT deptype, i.*
 (0 rows)
 
 DROP TABLE a;
+RESET search_path;
 DROP SCHEMA test_ext CASCADE;
 NOTICE:  drop cascades to extension test_ext5
+-- Fourth test: we can mark the objects as dependent, then unmark; then the
+-- drop of the extension does nothing
+SELECT * FROM test_extdep_commands \gexec
+ CREATE SCHEMA test_ext
+ CREATE EXTENSION test_ext5 SCHEMA test_ext
+ SET search_path TO test_ext
+ CREATE TABLE a (a1 int)
+
+ CREATE FUNCTION b() RETURNS TRIGGER LANGUAGE plpgsql AS
+   $$ BEGIN NEW.a1 := NEW.a1 + 42; RETURN NEW; END; $$
+ ALTER FUNCTION b() DEPENDS ON EXTENSION test_ext5
+
+ CREATE TRIGGER c BEFORE INSERT ON a FOR EACH ROW EXECUTE PROCEDURE b()
+ ALTER TRIGGER c ON a DEPENDS ON EXTENSION test_ext5
+
+ CREATE MATERIALIZED VIEW d AS SELECT * FROM a
+ ALTER MATERIALIZED VIEW d DEPENDS ON EXTENSION test_ext5
+
+ CREATE INDEX e ON a (a1)
+ ALTER INDEX e DEPENDS ON EXTENSION test_ext5
+ RESET search_path
+SET search_path TO test_ext;
+ALTER FUNCTION b() NO DEPENDS ON EXTENSION test_ext5;
+ALTER TRIGGER c ON a NO DEPENDS ON EXTENSION test_ext5;
+ALTER MATERIALIZED VIEW d NO DEPENDS ON EXTENSION test_ext5;
+ALTER INDEX e NO DEPENDS ON EXTENSION test_ext5;
+DROP EXTENSION test_ext5;
+DROP TRIGGER c ON a;
+DROP FUNCTION b();
+DROP MATERIALIZED VIEW d;
+DROP INDEX e;
+DROP SCHEMA test_ext CASCADE;
+NOTICE:  drop cascades to table a
index cc170ab70975c5b68ce0f8ef0f56417cfe7d730c..63240a1af5dfda5311d972d222710ebc10f92ed5 100644 (file)
@@ -70,6 +70,21 @@ SELECT deptype, i.*
         refobjid=(SELECT oid FROM pg_extension WHERE extname='test_ext5'))
    OR (refclassid='pg_class'::regclass AND refobjid='test_ext.a'::regclass)
    AND NOT deptype IN ('i', 'a');
-
 DROP TABLE a;
+RESET search_path;
+DROP SCHEMA test_ext CASCADE;
+
+-- Fourth test: we can mark the objects as dependent, then unmark; then the
+-- drop of the extension does nothing
+SELECT * FROM test_extdep_commands \gexec
+SET search_path TO test_ext;
+ALTER FUNCTION b() NO DEPENDS ON EXTENSION test_ext5;
+ALTER TRIGGER c ON a NO DEPENDS ON EXTENSION test_ext5;
+ALTER MATERIALIZED VIEW d NO DEPENDS ON EXTENSION test_ext5;
+ALTER INDEX e NO DEPENDS ON EXTENSION test_ext5;
+DROP EXTENSION test_ext5;
+DROP TRIGGER c ON a;
+DROP FUNCTION b();
+DROP MATERIALIZED VIEW d;
+DROP INDEX e;
 DROP SCHEMA test_ext CASCADE;