</listitem>
</varlistentry>
+ <varlistentry>
+ <term><symbol>DEPENDENCY_AUTO_EXTENSION</> (<literal>x</>)</term>
+ <listitem>
+ <para>
+ The dependent object is not a member of the extension that is the
+ referenced object (and so should not be ignored by pg_dump), but
+ cannot function without it and should be dropped when the
+ extension itself is. The dependent object may be dropped on its
+ own as well.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><symbol>DEPENDENCY_PIN</> (<literal>p</>)</term>
<listitem>
OWNER TO { <replaceable>new_owner</replaceable> | CURRENT_USER | SESSION_USER }
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>
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><replaceable class="parameter">extension_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of the extension that the function is to depend on.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>CALLED ON NULL INPUT</literal></term>
<term><literal>RETURNS NULL ON NULL INPUT</literal></term>
</programlisting>
</para>
+ <para>
+ To mark the function <literal>sqrt</literal> for type
+ <type>integer</type> as being dependent on the extension
+ <literal>mathlib</literal>:
+<programlisting>
+ALTER FUNCTION sqrt(integer) DEPENDS ON EXTENSION mathlib;
+</programlisting>
+ </para>
+
<para>
To adjust the search path that is automatically set for a function:
<programlisting>
<synopsis>
ALTER INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
ALTER INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> SET TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable>
+ALTER INDEX <replaceable class="PARAMETER">name</replaceable> DEPENDS ON EXTENSION <replaceable class="PARAMETER">extension_name</replaceable>
ALTER INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
ALTER INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
ALTER INDEX ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable> [ OWNED BY <replaceable class="PARAMETER">role_name</replaceable> [, ... ] ]
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>DEPENDS ON EXTENSION</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.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
<listitem>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><replaceable class="PARAMETER">extension_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of the extension that the index is to depend on.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><replaceable class="PARAMETER">storage_parameter</replaceable></term>
<listitem>
<synopsis>
ALTER MATERIALIZED VIEW [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
<replaceable class="PARAMETER">action</replaceable> [, ... ]
+ALTER MATERIALIZED VIEW <replaceable class="PARAMETER">name</replaceable>
+ DEPENDS ON EXTENSION <replaceable class="PARAMETER">extension_name</replaceable>
ALTER MATERIALIZED VIEW [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
RENAME [ COLUMN ] <replaceable class="PARAMETER">column_name</replaceable> TO <replaceable class="PARAMETER">new_column_name</replaceable>
ALTER MATERIALIZED VIEW [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
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
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><replaceable class="PARAMETER">extension_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of the extension that the materialized view is to depend on.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><replaceable class="PARAMETER">new_column_name</replaceable></term>
<listitem>
<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>
</synopsis>
</refsynopsisdiv>
<command>ALTER TRIGGER</command> changes properties of an existing
trigger. The <literal>RENAME</literal> clause changes the name of
the given trigger without otherwise changing the trigger
- definition.
+ definition. The <literal>DEPENDS ON EXTENSION</literal> clause marks
+ the trigger as dependent on an extension, such that if the extension is
+ dropped, the trigger will automatically be dropped as well.
</para>
<para>
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">extension_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of the extension that the trigger is to depend on.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
To rename an existing trigger:
<programlisting>
ALTER TRIGGER emp_stamp ON emp RENAME TO emp_track_chgs;
+</programlisting></para>
+
+ <para>
+ To mark a trigger as being dependent on an extension:
+<programlisting>
+ALTER TRIGGER emp_stamp ON emp DEPENDS ON EXTENSION emplib;
</programlisting></para>
</refsect1>
{
case DEPENDENCY_NORMAL:
case DEPENDENCY_AUTO:
+ case DEPENDENCY_AUTO_EXTENSION:
/* no problem */
break;
case DEPENDENCY_INTERNAL:
subflags = DEPFLAG_NORMAL;
break;
case DEPENDENCY_AUTO:
+ case DEPENDENCY_AUTO_EXTENSION:
subflags = DEPFLAG_AUTO;
break;
case DEPENDENCY_INTERNAL:
return address;
}
+/*
+ * Return an ObjectAddress based on a RangeVar and an object name. The
+ * name of the relation identified by the RangeVar is prepended to the
+ * (possibly empty) list passed in as objname. This is useful to find
+ * the ObjectAddress of objects that depend on a relation. All other
+ * considerations are exactly as for get_object_address above.
+ */
+ObjectAddress
+get_object_address_rv(ObjectType objtype, RangeVar *rel, List *objname,
+ List *objargs, Relation *relp, LOCKMODE lockmode,
+ bool missing_ok)
+{
+ if (rel)
+ {
+ objname = lcons(makeString(rel->relname), objname);
+ if (rel->schemaname)
+ objname = lcons(makeString(rel->schemaname), objname);
+ if (rel->catalogname)
+ objname = lcons(makeString(rel->catalogname), objname);
+ }
+
+ return get_object_address(objtype, objname, objargs,
+ relp, lockmode, missing_ok);
+}
+
/*
* Find an ObjectAddress for a type of object that is identified by an
* unqualified name.
}
}
+/*
+ * Executes an ALTER OBJECT / 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
+ * altered object now depends on.
+ */
+ObjectAddress
+ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddress)
+{
+ ObjectAddress address;
+ ObjectAddress refAddr;
+ Relation rel;
+
+ address =
+ get_object_address_rv(stmt->objectType, stmt->relation, stmt->objname,
+ stmt->objargs, &rel, AccessExclusiveLock, false);
+
+ /*
+ * If a relation was involved, it would have been opened and locked.
+ * We don't need the relation here, but we'll retain the lock until
+ * commit.
+ */
+ if (rel)
+ heap_close(rel, NoLock);
+
+ refAddr = get_object_address(OBJECT_EXTENSION, list_make1(stmt->extname),
+ NULL, &rel, AccessExclusiveLock, false);
+ Assert(rel == NULL);
+ if (refAddress)
+ *refAddress = refAddr;
+
+ recordDependencyOn(&address, refAddress, DEPENDENCY_AUTO_EXTENSION);
+
+ return address;
+}
+
/*
* Executes an ALTER OBJECT / SET SCHEMA statement. Based on the object
* type, the function appropriate to that type is executed.
return newnode;
}
+static AlterObjectDependsStmt *
+_copyAlterObjectDependsStmt(const AlterObjectDependsStmt *from)
+{
+ AlterObjectDependsStmt *newnode = makeNode(AlterObjectDependsStmt);
+
+ COPY_SCALAR_FIELD(objectType);
+ COPY_NODE_FIELD(relation);
+ COPY_NODE_FIELD(objname);
+ COPY_NODE_FIELD(objargs);
+ COPY_NODE_FIELD(extname);
+
+ return newnode;
+}
+
static AlterObjectSchemaStmt *
_copyAlterObjectSchemaStmt(const AlterObjectSchemaStmt *from)
{
case T_RenameStmt:
retval = _copyRenameStmt(from);
break;
+ case T_AlterObjectDependsStmt:
+ retval = _copyAlterObjectDependsStmt(from);
+ break;
case T_AlterObjectSchemaStmt:
retval = _copyAlterObjectSchemaStmt(from);
break;
return true;
}
+static bool
+_equalAlterObjectDependsStmt(const AlterObjectDependsStmt *a, const AlterObjectDependsStmt *b)
+{
+ COMPARE_SCALAR_FIELD(objectType);
+ COMPARE_NODE_FIELD(relation);
+ COMPARE_NODE_FIELD(objname);
+ COMPARE_NODE_FIELD(objargs);
+ COMPARE_NODE_FIELD(extname);
+
+ return true;
+}
+
static bool
_equalAlterObjectSchemaStmt(const AlterObjectSchemaStmt *a, const AlterObjectSchemaStmt *b)
{
case T_RenameStmt:
retval = _equalRenameStmt(a, b);
break;
+ case T_AlterObjectDependsStmt:
+ retval = _equalAlterObjectDependsStmt(a, b);
+ break;
case T_AlterObjectSchemaStmt:
retval = _equalAlterObjectSchemaStmt(a, b);
break;
AlterEventTrigStmt
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
- AlterObjectSchemaStmt AlterOwnerStmt AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
+ AlterObjectDependsStmt AlterObjectSchemaStmt AlterOwnerStmt
+ AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
AlterTblSpcStmt AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt
CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
- DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DESC
+ DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DEPENDS DESC
DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
| AlterForeignTableStmt
| AlterFunctionStmt
| AlterGroupStmt
+ | AlterObjectDependsStmt
| AlterObjectSchemaStmt
| AlterOwnerStmt
| AlterOperatorStmt
| /*EMPTY*/ { $$ = 0; }
;
+/*****************************************************************************
+ *
+ * ALTER THING name DEPENDS ON EXTENSION name
+ *
+ *****************************************************************************/
+
+AlterObjectDependsStmt:
+ ALTER FUNCTION function_with_argtypes DEPENDS ON EXTENSION name
+ {
+ AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
+ n->objectType = OBJECT_FUNCTION;
+ n->relation = NULL;
+ n->objname = $3->funcname;
+ n->objargs = $3->funcargs;
+ n->extname = makeString($7);
+ $$ = (Node *)n;
+ }
+ | ALTER TRIGGER name ON qualified_name DEPENDS ON EXTENSION name
+ {
+ AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
+ n->objectType = OBJECT_TRIGGER;
+ n->relation = $5;
+ n->objname = list_make1(makeString($3));
+ n->objargs = NIL;
+ n->extname = makeString($9);
+ $$ = (Node *)n;
+ }
+ | ALTER MATERIALIZED VIEW qualified_name DEPENDS ON EXTENSION name
+ {
+ AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
+ n->objectType = OBJECT_MATVIEW;
+ n->relation = $4;
+ n->objname = NIL;
+ n->objargs = NIL;
+ n->extname = makeString($8);
+ $$ = (Node *)n;
+ }
+ | ALTER INDEX qualified_name DEPENDS ON EXTENSION name
+ {
+ AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
+ n->objectType = OBJECT_INDEX;
+ n->relation = $3;
+ n->objname = NIL;
+ n->objargs = NIL;
+ n->extname = makeString($7);
+ $$ = (Node *)n;
+ }
+ ;
+
/*****************************************************************************
*
* ALTER THING name SET SCHEMA name
| DELETE_P
| DELIMITER
| DELIMITERS
+ | DEPENDS
| DICTIONARY
| DISABLE_P
| DISCARD
case T_AlterFunctionStmt:
case T_AlterRoleStmt:
case T_AlterRoleSetStmt:
+ case T_AlterObjectDependsStmt:
case T_AlterObjectSchemaStmt:
case T_AlterOwnerStmt:
case T_AlterOperatorStmt:
}
break;
+ case T_AlterObjectDependsStmt:
+ {
+ AlterObjectDependsStmt *stmt = (AlterObjectDependsStmt *) parsetree;
+
+ if (EventTriggerSupportsObjectType(stmt->objectType))
+ ProcessUtilitySlow(parsetree, queryString,
+ context, params,
+ dest, completionTag);
+ else
+ ExecAlterObjectDependsStmt(stmt, NULL);
+ }
+ break;
+
case T_AlterObjectSchemaStmt:
{
AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parsetree;
address = ExecRenameStmt((RenameStmt *) parsetree);
break;
+ case T_AlterObjectDependsStmt:
+ address =
+ ExecAlterObjectDependsStmt((AlterObjectDependsStmt *) parsetree,
+ &secondaryObject);
+ break;
+
case T_AlterObjectSchemaStmt:
address =
ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree,
tag = AlterObjectTypeCommandTag(((RenameStmt *) parsetree)->renameType);
break;
+ case T_AlterObjectDependsStmt:
+ tag = AlterObjectTypeCommandTag(((AlterObjectDependsStmt *) parsetree)->objectType);
+ break;
+
case T_AlterObjectSchemaStmt:
tag = AlterObjectTypeCommandTag(((AlterObjectSchemaStmt *) parsetree)->objectType);
break;
lev = LOGSTMT_DDL;
break;
+ case T_AlterObjectDependsStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_AlterObjectSchemaStmt:
lev = LOGSTMT_DDL;
break;
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201604052
+#define CATALOG_VERSION_NO 201604053
#endif
* this dependency type acts the same as an internal dependency, but it's
* kept separate for clarity and to simplify pg_dump.
*
+ * DEPENDENCY_AUTO_EXTENSION ('x'): the dependent object is not a member
+ * of the extension that is the referenced object (and so should not be
+ * ignored by pg_dump), but cannot function without the extension and
+ * should be dropped when the extension itself is. The dependent object
+ * may be dropped on its own as well.
+ *
* DEPENDENCY_PIN ('p'): there is no dependent object; this type of entry
* is a signal that the system itself depends on the referenced object,
* and so that object must never be deleted. Entries of this type are
DEPENDENCY_AUTO = 'a',
DEPENDENCY_INTERNAL = 'i',
DEPENDENCY_EXTENSION = 'e',
+ DEPENDENCY_AUTO_EXTENSION = 'x',
DEPENDENCY_PIN = 'p'
} DependencyType;
List *objargs, Relation *relp,
LOCKMODE lockmode, bool missing_ok);
+extern ObjectAddress get_object_address_rv(ObjectType objtype, RangeVar *rel,
+ List *objname, List *objargs, Relation *relp,
+ LOCKMODE lockmode, bool missing_ok);
+
extern void check_object_ownership(Oid roleid,
ObjectType objtype, ObjectAddress address,
List *objname, List *objargs, Relation relation);
extern ObjectAddress ExecRenameStmt(RenameStmt *stmt);
+extern ObjectAddress ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt,
+ ObjectAddress *refAddress);
extern ObjectAddress ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
ObjectAddress *oldSchemaAddr);
extern Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
T_DeclareCursorStmt,
T_CreateTableSpaceStmt,
T_DropTableSpaceStmt,
+ T_AlterObjectDependsStmt,
T_AlterObjectSchemaStmt,
T_AlterOwnerStmt,
T_AlterOperatorStmt,
bool missing_ok; /* skip error if missing? */
} RenameStmt;
+/* ----------------------
+ * ALTER object DEPENDS ON EXTENSION extname
+ * ----------------------
+ */
+typedef struct AlterObjectDependsStmt
+{
+ NodeTag type;
+ ObjectType objectType; /* OBJECT_FUNCTION, OBJECT_TRIGGER, etc */
+ RangeVar *relation; /* in case a table is involved */
+ List *objname; /* name of the object */
+ List *objargs; /* argument types, if applicable */
+ Value *extname; /* extension name */
+} AlterObjectDependsStmt;
+
/* ----------------------
* ALTER object SET SCHEMA Statement
* ----------------------
PG_KEYWORD("delete", DELETE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("delimiter", DELIMITER, UNRESERVED_KEYWORD)
PG_KEYWORD("delimiters", DELIMITERS, UNRESERVED_KEYWORD)
+PG_KEYWORD("depends", DEPENDS, UNRESERVED_KEYWORD)
PG_KEYWORD("desc", DESC, RESERVED_KEYWORD)
PG_KEYWORD("dictionary", DICTIONARY, UNRESERVED_KEYWORD)
PG_KEYWORD("disable", DISABLE_P, UNRESERVED_KEYWORD)
test_ext4--1.0.sql test_ext5--1.0.sql test_ext_cyclic1--1.0.sql \
test_ext_cyclic2--1.0.sql
-REGRESS = test_extensions
+REGRESS = test_extensions test_extdepend
ifdef USE_PGXS
PG_CONFIG = pg_config
--- /dev/null
+--
+-- test ALTER THING name DEPENDS ON EXTENSION
+--
+-- Common setup for all tests
+CREATE TABLE test_extdep_commands (command text);
+COPY test_extdep_commands FROM stdin;
+SELECT * FROM test_extdep_commands;
+ command
+-------------------------------------------------------------------------
+ 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
+(17 rows)
+
+-- First, test that dependent objects go away when the extension is dropped.
+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
+-- make sure we have the right dependencies on the extension
+SELECT deptype, p.*
+ FROM pg_depend, pg_identify_object(classid, objid, objsubid) AS p
+ WHERE refclassid = 'pg_extension'::regclass AND
+ refobjid = (SELECT oid FROM pg_extension WHERE extname = 'test_ext5')
+ORDER BY type;
+ deptype | type | schema | name | identity
+---------+-------------------+----------+------+-----------------
+ x | function | test_ext | | test_ext.b()
+ x | index | test_ext | e | test_ext.e
+ x | materialized view | test_ext | d | test_ext.d
+ x | trigger | | | c on test_ext.a
+(4 rows)
+
+DROP EXTENSION test_ext5;
+-- anything still depending on the table?
+SELECT deptype, i.*
+ FROM pg_catalog.pg_depend, pg_identify_object(classid, objid, objsubid) i
+WHERE refclassid='pg_class'::regclass AND
+ refobjid='test_ext.a'::regclass AND NOT deptype IN ('i', 'a');
+ deptype | type | schema | name | identity
+---------+------+--------+------+----------
+(0 rows)
+
+DROP SCHEMA test_ext CASCADE;
+NOTICE: drop cascades to table test_ext.a
+-- Second test: If we drop the table, the objects are dropped too and no
+-- vestige remains in pg_depend.
+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
+DROP TABLE test_ext.a; -- should fail, require cascade
+ERROR: cannot drop table test_ext.a because other objects depend on it
+DETAIL: materialized view test_ext.d depends on table test_ext.a
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+DROP TABLE test_ext.a CASCADE;
+NOTICE: drop cascades to materialized view test_ext.d
+-- anything still depending on the extension? Should be only function b()
+SELECT deptype, i.*
+ FROM pg_catalog.pg_depend, pg_identify_object(classid, objid, objsubid) i
+ WHERE refclassid='pg_extension'::regclass AND
+ refobjid=(SELECT oid FROM pg_extension WHERE extname='test_ext5');
+ deptype | type | schema | name | identity
+---------+----------+----------+------+--------------
+ x | function | test_ext | | test_ext.b()
+(1 row)
+
+DROP EXTENSION test_ext5;
+DROP SCHEMA test_ext CASCADE;
+-- Third test: we can drop the objects individually
+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;
+DROP TRIGGER c ON a;
+DROP FUNCTION b();
+DROP MATERIALIZED VIEW d;
+DROP INDEX e;
+SELECT deptype, i.*
+ FROM pg_catalog.pg_depend, pg_identify_object(classid, objid, objsubid) i
+ WHERE (refclassid='pg_extension'::regclass AND
+ 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');
+ deptype | type | schema | name | identity
+---------+------+--------+------+----------
+(0 rows)
+
+DROP TABLE a;
+DROP SCHEMA test_ext CASCADE;
+NOTICE: drop cascades to extension test_ext5
--- /dev/null
+--
+-- test ALTER THING name DEPENDS ON EXTENSION
+--
+
+-- Common setup for all tests
+CREATE TABLE test_extdep_commands (command text);
+COPY test_extdep_commands FROM stdin;
+ 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\n $$ 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
+\.
+
+SELECT * FROM test_extdep_commands;
+-- First, test that dependent objects go away when the extension is dropped.
+SELECT * FROM test_extdep_commands \gexec
+-- make sure we have the right dependencies on the extension
+SELECT deptype, p.*
+ FROM pg_depend, pg_identify_object(classid, objid, objsubid) AS p
+ WHERE refclassid = 'pg_extension'::regclass AND
+ refobjid = (SELECT oid FROM pg_extension WHERE extname = 'test_ext5')
+ORDER BY type;
+DROP EXTENSION test_ext5;
+-- anything still depending on the table?
+SELECT deptype, i.*
+ FROM pg_catalog.pg_depend, pg_identify_object(classid, objid, objsubid) i
+WHERE refclassid='pg_class'::regclass AND
+ refobjid='test_ext.a'::regclass AND NOT deptype IN ('i', 'a');
+DROP SCHEMA test_ext CASCADE;
+
+-- Second test: If we drop the table, the objects are dropped too and no
+-- vestige remains in pg_depend.
+SELECT * FROM test_extdep_commands \gexec
+DROP TABLE test_ext.a; -- should fail, require cascade
+DROP TABLE test_ext.a CASCADE;
+-- anything still depending on the extension? Should be only function b()
+SELECT deptype, i.*
+ FROM pg_catalog.pg_depend, pg_identify_object(classid, objid, objsubid) i
+ WHERE refclassid='pg_extension'::regclass AND
+ refobjid=(SELECT oid FROM pg_extension WHERE extname='test_ext5');
+DROP EXTENSION test_ext5;
+DROP SCHEMA test_ext CASCADE;
+
+-- Third test: we can drop the objects individually
+SELECT * FROM test_extdep_commands \gexec
+SET search_path TO test_ext;
+DROP TRIGGER c ON a;
+DROP FUNCTION b();
+DROP MATERIALIZED VIEW d;
+DROP INDEX e;
+
+SELECT deptype, i.*
+ FROM pg_catalog.pg_depend, pg_identify_object(classid, objid, objsubid) i
+ WHERE (refclassid='pg_extension'::regclass AND
+ 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;
+DROP SCHEMA test_ext CASCADE;
AlterFdwStmt
AlterForeignServerStmt
AlterFunctionStmt
+AlterObjectDependsStmt
AlterObjectSchemaStmt
AlterOpFamilyStmt
AlterOwnerStmt