On 2020-Feb-17, Alvaro Herrera wrote: > * More than one 'x' dependencies are allowed for the same object on the > same extension. That's useless and polluting, so should be prevented. > > * There's no way to remove an 'x' dependency.
Here's these two patches. There's an "if (true)" in 0002 which is a little weird -- that's there just to avoid reindenting those lines in 0003. In principle, I would think that these are all backpatchable bugfixes. Maybe 0002 could pass as not backpatchable since it disallows a command that works today. OTOH the feature is rarely used, so maybe a backpatch is not welcome anyhow. -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>From 586899b994e45a2b2bf217a25198ba4bdbc6ee82 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvhe...@alvh.no-ip.org> Date: Mon, 17 Feb 2020 19:42:57 -0300 Subject: [PATCH 2/3] Avoid duplicates in ALTER ... DEPENDS ON EXTENSION If the command is attempted on an extension that the object already depends on, raise an error. --- src/backend/catalog/pg_depend.c | 43 ++++++++++++++++++++++++++++++++ src/backend/commands/alter.c | 17 ++++++++++++- src/include/catalog/dependency.h | 1 + 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index f9af245eec..596dafe19c 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -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 * diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 1cb84182b0..28e32f2fb5 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -462,7 +462,22 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre if (refAddress) *refAddress = refAddr; - recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION); + if (true) + { + List *currexts; + + /* Avoid duplicates */ + currexts = getAutoExtensionsOfObject(address.classId, + address.objectId); + if (list_member_oid(currexts, refAddr.objectId)) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("%s already depends on extension \"%s\"", + getObjectDescription(&address), + get_extension_name(refAddr.objectId)))); + + recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION); + } return address; } diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 0cd6fcf027..ab5e92bdc6 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -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); -- 2.20.1
>From 540941aa2d8a3bd84518dbd49600dc013771c275 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvhe...@alvh.no-ip.org> Date: Wed, 30 Oct 2019 16:57:29 -0300 Subject: [PATCH 3/3] Add ALTER .. NO DEPENDS ON This new command removes any previously added dependency mark; necessary for completeness. --- doc/src/sgml/ref/alter_function.sgml | 10 ++-- doc/src/sgml/ref/alter_index.sgml | 9 ++-- doc/src/sgml/ref/alter_materialized_view.sgml | 11 ++--- doc/src/sgml/ref/alter_trigger.sgml | 7 ++- src/backend/catalog/pg_depend.c | 49 +++++++++++++++++++ src/backend/commands/alter.c | 17 ++++++- src/backend/nodes/copyfuncs.c | 1 + src/backend/nodes/equalfuncs.c | 1 + src/backend/parser/gram.y | 36 +++++++++----- src/include/catalog/dependency.h | 4 ++ src/include/nodes/parsenodes.h | 1 + .../expected/test_extdepend.out | 39 ++++++++++++++- .../test_extensions/sql/test_extdepend.sql | 18 ++++++- 13 files changed, 169 insertions(+), 34 deletions(-) diff --git a/doc/src/sgml/ref/alter_function.sgml b/doc/src/sgml/ref/alter_function.sgml index 03ffa5945a..70b1f24bc0 100644 --- a/doc/src/sgml/ref/alter_function.sgml +++ b/doc/src/sgml/ref/alter_function.sgml @@ -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> diff --git a/doc/src/sgml/ref/alter_index.sgml b/doc/src/sgml/ref/alter_index.sgml index 6d34dbb74e..de6f89d458 100644 --- a/doc/src/sgml/ref/alter_index.sgml +++ b/doc/src/sgml/ref/alter_index.sgml @@ -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> diff --git a/doc/src/sgml/ref/alter_materialized_view.sgml b/doc/src/sgml/ref/alter_materialized_view.sgml index 03e3df1ffd..9df8a79977 100644 --- a/doc/src/sgml/ref/alter_materialized_view.sgml +++ b/doc/src/sgml/ref/alter_materialized_view.sgml @@ -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> diff --git a/doc/src/sgml/ref/alter_trigger.sgml b/doc/src/sgml/ref/alter_trigger.sgml index 6cf789a67a..6d4784c82f 100644 --- a/doc/src/sgml/ref/alter_trigger.sgml +++ b/doc/src/sgml/ref/alter_trigger.sgml @@ -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> diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 596dafe19c..fa38ee9477 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -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 * diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 28e32f2fb5..4a98e11efe 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -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 @@ -462,10 +462,23 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre if (refAddress) *refAddress = refAddr; - if (true) + if (stmt->remove) + deleteDependencyRecordsForSpecific(address.classId, address.objectId, + DEPENDENCY_AUTO_EXTENSION, + refAddr.classId, refAddr.objectId); + else { + Oid currextoid; List *currexts; + /* Fail if object is part of an extension */ + currextoid = getExtensionOfObject(address.classId, address.objectId); + if (OidIsValid(currextoid)) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("%s is already part of extension \"%s\"", + getObjectDescription(&address), + get_extension_name(currextoid)))); /* Avoid duplicates */ currexts = getAutoExtensionsOfObject(address.classId, address.objectId); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 54ad62bb7f..851e0b0c4a 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3593,6 +3593,7 @@ _copyAlterObjectDependsStmt(const AlterObjectDependsStmt *from) COPY_NODE_FIELD(relation); COPY_NODE_FIELD(object); COPY_NODE_FIELD(extname); + COPY_SCALAR_FIELD(remove); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 5b1ba143b1..8c734045a8 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1445,6 +1445,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; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1b0edf5d3d..d99156194d 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -311,7 +311,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 @@ -9024,57 +9024,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 diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index ab5e92bdc6..2c6abe26a5 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -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); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index da0706add5..dad344f137 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -2930,6 +2930,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; /* ---------------------- diff --git a/src/test/modules/test_extensions/expected/test_extdepend.out b/src/test/modules/test_extensions/expected/test_extdepend.out index 11e441ddd3..7bb1d8f362 100644 --- a/src/test/modules/test_extensions/expected/test_extdepend.out +++ b/src/test/modules/test_extensions/expected/test_extdepend.out @@ -147,6 +147,41 @@ SELECT deptype, i.* ---------+------+--------+------+---------- (0 rows) -DROP TABLE a; +RESET search_path; DROP SCHEMA test_ext CASCADE; -NOTICE: drop cascades to extension test_ext5 +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to extension test_ext5 +drop cascades to table test_ext.a +-- 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 diff --git a/src/test/modules/test_extensions/sql/test_extdepend.sql b/src/test/modules/test_extensions/sql/test_extdepend.sql index cf44145dcb..b709d71e96 100644 --- a/src/test/modules/test_extensions/sql/test_extdepend.sql +++ b/src/test/modules/test_extensions/sql/test_extdepend.sql @@ -68,6 +68,20 @@ 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; -- 2.20.1