Commit 5fc70394 authored by Alvaro Herrera's avatar Alvaro Herrera

Add ALTER .. NO DEPENDS ON

Commit f2fcad27 (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.pgsqlReviewed-by: default avatarahsan hadi <ahsan.hadi@gmail.com>
Reviewed-by: default avatarIbrar Ahmed <ibrar.ahmad@gmail.com>
Reviewed-by: default avatarTom Lane <tgl@sss.pgh.pa.us>
parent 4157f73b
...@@ -30,7 +30,7 @@ ALTER FUNCTION <replaceable>name</replaceable> [ ( [ [ <replaceable class="param ...@@ -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> [, ...] ] ) ] 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> 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> [, ...] ] ) ] 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> <phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase>
...@@ -153,10 +153,14 @@ ALTER FUNCTION <replaceable>name</replaceable> [ ( [ [ <replaceable class="param ...@@ -153,10 +153,14 @@ ALTER FUNCTION <replaceable>name</replaceable> [ ( [ [ <replaceable class="param
</varlistentry> </varlistentry>
<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> <listitem>
<para> <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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -100,11 +100,14 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="parameter">name</replaceable> ...@@ -100,11 +100,14 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="parameter">name</replaceable>
</varlistentry> </varlistentry>
<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> <listitem>
<para> <para>
This form marks the index as dependent on the extension, such that if the This form marks the index as dependent on the extension, or no longer
extension is dropped, the index will automatically be dropped as well. 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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -68,12 +68,6 @@ ALTER MATERIALIZED VIEW ALL IN TABLESPACE <replaceable class="parameter">name</r ...@@ -68,12 +68,6 @@ ALTER MATERIALIZED VIEW ALL IN TABLESPACE <replaceable class="parameter">name</r
anyway.) anyway.)
</para> </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> <para>
The statement subforms and actions available for The statement subforms and actions available for
<command>ALTER MATERIALIZED VIEW</command> are a subset of those available <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 ...@@ -110,7 +104,10 @@ ALTER MATERIALIZED VIEW ALL IN TABLESPACE <replaceable class="parameter">name</r
<term><replaceable class="parameter">extension_name</replaceable></term> <term><replaceable class="parameter">extension_name</replaceable></term>
<listitem> <listitem>
<para> <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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -22,7 +22,7 @@ PostgreSQL documentation ...@@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <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> 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> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -78,7 +78,10 @@ ALTER TRIGGER <replaceable class="parameter">name</replaceable> ON <replaceable ...@@ -78,7 +78,10 @@ ALTER TRIGGER <replaceable class="parameter">name</replaceable> ON <replaceable
<term><replaceable class="parameter">extension_name</replaceable></term> <term><replaceable class="parameter">extension_name</replaceable></term>
<listitem> <listitem>
<para> <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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -278,6 +278,55 @@ deleteDependencyRecordsForClass(Oid classId, Oid objectId, ...@@ -278,6 +278,55 @@ deleteDependencyRecordsForClass(Oid classId, Oid objectId,
return count; 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 * Adjust dependency record(s) to point to a different object of the same type
* *
......
...@@ -421,7 +421,7 @@ ExecRenameStmt(RenameStmt *stmt) ...@@ -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 * 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 * argument which, if not null, receives the address of the object that the
...@@ -433,7 +433,6 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre ...@@ -433,7 +433,6 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre
ObjectAddress address; ObjectAddress address;
ObjectAddress refAddr; ObjectAddress refAddr;
Relation rel; Relation rel;
List *currexts;
address = address =
get_object_address_rv(stmt->objectType, stmt->relation, (List *) stmt->object, get_object_address_rv(stmt->objectType, stmt->relation, (List *) stmt->object,
...@@ -463,11 +462,22 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre ...@@ -463,11 +462,22 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre
if (refAddress) if (refAddress)
*refAddress = refAddr; *refAddress = refAddr;
if (stmt->remove)
{
deleteDependencyRecordsForSpecific(address.classId, address.objectId,
DEPENDENCY_AUTO_EXTENSION,
refAddr.classId, refAddr.objectId);
}
else
{
List *currexts;
/* Avoid duplicates */ /* Avoid duplicates */
currexts = getAutoExtensionsOfObject(address.classId, currexts = getAutoExtensionsOfObject(address.classId,
address.objectId); address.objectId);
if (!list_member_oid(currexts, refAddr.objectId)) if (!list_member_oid(currexts, refAddr.objectId))
recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION); recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
}
return address; return address;
} }
......
...@@ -3638,6 +3638,7 @@ _copyAlterObjectDependsStmt(const AlterObjectDependsStmt *from) ...@@ -3638,6 +3638,7 @@ _copyAlterObjectDependsStmt(const AlterObjectDependsStmt *from)
COPY_NODE_FIELD(relation); COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(object); COPY_NODE_FIELD(object);
COPY_NODE_FIELD(extname); COPY_NODE_FIELD(extname);
COPY_SCALAR_FIELD(remove);
return newnode; return newnode;
} }
......
...@@ -1449,6 +1449,7 @@ _equalAlterObjectDependsStmt(const AlterObjectDependsStmt *a, const AlterObjectD ...@@ -1449,6 +1449,7 @@ _equalAlterObjectDependsStmt(const AlterObjectDependsStmt *a, const AlterObjectD
COMPARE_NODE_FIELD(relation); COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(object); COMPARE_NODE_FIELD(object);
COMPARE_NODE_FIELD(extname); COMPARE_NODE_FIELD(extname);
COMPARE_SCALAR_FIELD(remove);
return true; return true;
} }
......
...@@ -320,7 +320,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); ...@@ -320,7 +320,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <list> vac_analyze_option_list %type <list> vac_analyze_option_list
%type <node> vac_analyze_option_arg %type <node> vac_analyze_option_arg
%type <defelt> drop_option %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_grant_grant_option opt_grant_admin_option
opt_nowait opt_if_exists opt_with_data opt_nowait opt_if_exists opt_with_data
opt_transaction_chain opt_transaction_chain
...@@ -9053,57 +9053,67 @@ opt_set_data: SET DATA_P { $$ = 1; } ...@@ -9053,57 +9053,67 @@ opt_set_data: SET DATA_P { $$ = 1; }
*****************************************************************************/ *****************************************************************************/
AlterObjectDependsStmt: 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); AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
n->objectType = OBJECT_FUNCTION; n->objectType = OBJECT_FUNCTION;
n->object = (Node *) $3; n->object = (Node *) $3;
n->extname = makeString($7); n->extname = makeString($8);
n->remove = $4;
$$ = (Node *)n; $$ = (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); AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
n->objectType = OBJECT_PROCEDURE; n->objectType = OBJECT_PROCEDURE;
n->object = (Node *) $3; n->object = (Node *) $3;
n->extname = makeString($7); n->extname = makeString($8);
n->remove = $4;
$$ = (Node *)n; $$ = (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); AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
n->objectType = OBJECT_ROUTINE; n->objectType = OBJECT_ROUTINE;
n->object = (Node *) $3; n->object = (Node *) $3;
n->extname = makeString($7); n->extname = makeString($8);
n->remove = $4;
$$ = (Node *)n; $$ = (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); AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
n->objectType = OBJECT_TRIGGER; n->objectType = OBJECT_TRIGGER;
n->relation = $5; n->relation = $5;
n->object = (Node *) list_make1(makeString($3)); n->object = (Node *) list_make1(makeString($3));
n->extname = makeString($9); n->extname = makeString($10);
n->remove = $6;
$$ = (Node *)n; $$ = (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); AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
n->objectType = OBJECT_MATVIEW; n->objectType = OBJECT_MATVIEW;
n->relation = $4; n->relation = $4;
n->extname = makeString($8); n->extname = makeString($9);
n->remove = $5;
$$ = (Node *)n; $$ = (Node *)n;
} }
| ALTER INDEX qualified_name DEPENDS ON EXTENSION name | ALTER INDEX qualified_name opt_no DEPENDS ON EXTENSION name
{ {
AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt); AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
n->objectType = OBJECT_INDEX; n->objectType = OBJECT_INDEX;
n->relation = $3; n->relation = $3;
n->extname = makeString($7); n->extname = makeString($8);
n->remove = $4;
$$ = (Node *)n; $$ = (Node *)n;
} }
; ;
opt_no: NO { $$ = true; }
| /* EMPTY */ { $$ = false; }
;
/***************************************************************************** /*****************************************************************************
* *
* ALTER THING name SET SCHEMA name * ALTER THING name SET SCHEMA name
......
...@@ -196,6 +196,10 @@ extern long deleteDependencyRecordsFor(Oid classId, Oid objectId, ...@@ -196,6 +196,10 @@ extern long deleteDependencyRecordsFor(Oid classId, Oid objectId,
extern long deleteDependencyRecordsForClass(Oid classId, Oid objectId, extern long deleteDependencyRecordsForClass(Oid classId, Oid objectId,
Oid refclassId, char deptype); Oid refclassId, char deptype);
extern long deleteDependencyRecordsForSpecific(Oid classId, Oid objectId,
char deptype,
Oid refclassId, Oid refobjectId);
extern long changeDependencyFor(Oid classId, Oid objectId, extern long changeDependencyFor(Oid classId, Oid objectId,
Oid refClassId, Oid oldRefObjectId, Oid refClassId, Oid oldRefObjectId,
Oid newRefObjectId); Oid newRefObjectId);
......
...@@ -2936,6 +2936,7 @@ typedef struct AlterObjectDependsStmt ...@@ -2936,6 +2936,7 @@ typedef struct AlterObjectDependsStmt
RangeVar *relation; /* in case a table is involved */ RangeVar *relation; /* in case a table is involved */
Node *object; /* name of the object */ Node *object; /* name of the object */
Value *extname; /* extension name */ Value *extname; /* extension name */
bool remove; /* set true to remove dep rather than add */
} AlterObjectDependsStmt; } AlterObjectDependsStmt;
/* ---------------------- /* ----------------------
......
...@@ -150,5 +150,39 @@ SELECT deptype, i.* ...@@ -150,5 +150,39 @@ SELECT deptype, i.*
(0 rows) (0 rows)
DROP TABLE a; DROP TABLE a;
RESET search_path;
DROP SCHEMA test_ext CASCADE; DROP SCHEMA test_ext CASCADE;
NOTICE: drop cascades to extension test_ext5 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
...@@ -70,6 +70,21 @@ SELECT deptype, i.* ...@@ -70,6 +70,21 @@ SELECT deptype, i.*
refobjid=(SELECT oid FROM pg_extension WHERE extname='test_ext5')) refobjid=(SELECT oid FROM pg_extension WHERE extname='test_ext5'))
OR (refclassid='pg_class'::regclass AND refobjid='test_ext.a'::regclass) OR (refclassid='pg_class'::regclass AND refobjid='test_ext.a'::regclass)
AND NOT deptype IN ('i', 'a'); AND NOT deptype IN ('i', 'a');
DROP TABLE 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; DROP SCHEMA test_ext CASCADE;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment