diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index bb752294c3..725bacee5d 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2876,6 +2876,19 @@
+
+ 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 it and should be dropped when the
+ extension itself is. The dependent object may be dropped on its
+ own as well.
+
+
+
+
DEPENDENCY_PIN> (p>)
diff --git a/doc/src/sgml/ref/alter_function.sgml b/doc/src/sgml/ref/alter_function.sgml
index f40363e4aa..865d52b591 100644
--- a/doc/src/sgml/ref/alter_function.sgml
+++ b/doc/src/sgml/ref/alter_function.sgml
@@ -29,6 +29,8 @@ ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] )
SET SCHEMA new_schema
+ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] )
+ DEPENDS ON EXTENSION extension_name
where action is one of:
@@ -148,6 +150,15 @@ ALTER FUNCTION name ( [ [ extension_name
+
+
+ The name of the extension that the function is to depend on.
+
+
+
+
CALLED ON NULL INPUT
RETURNS NULL ON NULL INPUT
@@ -299,6 +310,15 @@ ALTER FUNCTION sqrt(integer) SET SCHEMA maths;
+
+ To mark the function sqrt for type
+ integer as being dependent on the extension
+ mathlib:
+
+ALTER FUNCTION sqrt(integer) DEPENDS ON EXTENSION mathlib;
+
+
+
To adjust the search path that is automatically set for a function:
diff --git a/doc/src/sgml/ref/alter_index.sgml b/doc/src/sgml/ref/alter_index.sgml
index ee3e3de4d6..54f3a56d3c 100644
--- a/doc/src/sgml/ref/alter_index.sgml
+++ b/doc/src/sgml/ref/alter_index.sgml
@@ -23,6 +23,7 @@ PostgreSQL documentation
ALTER INDEX [ IF EXISTS ] name RENAME TO new_name
ALTER INDEX [ IF EXISTS ] name SET TABLESPACE tablespace_name
+ALTER INDEX name DEPENDS ON EXTENSION extension_name
ALTER INDEX [ IF EXISTS ] name SET ( storage_parameter = value [, ... ] )
ALTER INDEX [ IF EXISTS ] name RESET ( storage_parameter [, ... ] )
ALTER INDEX ALL IN TABLESPACE name [ OWNED BY role_name [, ... ] ]
@@ -82,6 +83,16 @@ ALTER INDEX ALL IN TABLESPACE name
+
+ DEPENDS ON EXTENSION
+
+
+ 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.
+
+
+
+
SET ( storage_parameter = value [, ... ] )
@@ -147,6 +158,15 @@ ALTER INDEX ALL IN TABLESPACE name
+
+ extension_name
+
+
+ The name of the extension that the index is to depend on.
+
+
+
+
storage_parameter
diff --git a/doc/src/sgml/ref/alter_materialized_view.sgml b/doc/src/sgml/ref/alter_materialized_view.sgml
index 8807e01c36..b5c44bfabf 100644
--- a/doc/src/sgml/ref/alter_materialized_view.sgml
+++ b/doc/src/sgml/ref/alter_materialized_view.sgml
@@ -23,6 +23,8 @@ PostgreSQL documentation
ALTER MATERIALIZED VIEW [ IF EXISTS ] name
action [, ... ]
+ALTER MATERIALIZED VIEW name
+ DEPENDS ON EXTENSION extension_name
ALTER MATERIALIZED VIEW [ IF EXISTS ] name
RENAME [ COLUMN ] column_name TO new_column_name
ALTER MATERIALIZED VIEW [ IF EXISTS ] name
@@ -67,6 +69,12 @@ ALTER MATERIALIZED VIEW ALL IN TABLESPACE name
+
+ The DEPENDS ON EXTENSION form marks the materialized view
+ as dependent on an extension, such that the materialized view will
+ automatically be dropped if the extension is dropped.
+
+
The statement subforms and actions available for
ALTER MATERIALIZED VIEW are a subset of those available
@@ -99,6 +107,15 @@ ALTER MATERIALIZED VIEW ALL IN TABLESPACE name
+
+ extension_name
+
+
+ The name of the extension that the materialized view is to depend on.
+
+
+
+
new_column_name
diff --git a/doc/src/sgml/ref/alter_trigger.sgml b/doc/src/sgml/ref/alter_trigger.sgml
index c63b5dfa02..47eef6e5e8 100644
--- a/doc/src/sgml/ref/alter_trigger.sgml
+++ b/doc/src/sgml/ref/alter_trigger.sgml
@@ -22,6 +22,7 @@ PostgreSQL documentation
ALTER TRIGGER name ON table_name RENAME TO new_name
+ALTER TRIGGER name ON table_name DEPENDS ON EXTENSION extension_name
@@ -32,7 +33,9 @@ ALTER TRIGGER name ON ALTER TRIGGER changes properties of an existing
trigger. The RENAME clause changes the name of
the given trigger without otherwise changing the trigger
- definition.
+ definition. The DEPENDS ON EXTENSION clause marks
+ the trigger as dependent on an extension, such that if the extension is
+ dropped, the trigger will automatically be dropped as well.
@@ -70,6 +73,15 @@ ALTER TRIGGER name ON
+
+
+ extension_name
+
+
+ The name of the extension that the trigger is to depend on.
+
+
+
@@ -92,6 +104,12 @@ ALTER TRIGGER name ON
ALTER TRIGGER emp_stamp ON emp RENAME TO emp_track_chgs;
+
+
+
+ To mark a trigger as being dependent on an extension:
+
+ALTER TRIGGER emp_stamp ON emp DEPENDS ON EXTENSION emplib;
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 17f9de1ff9..79595a9d23 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -589,6 +589,7 @@ findDependentObjects(const ObjectAddress *object,
{
case DEPENDENCY_NORMAL:
case DEPENDENCY_AUTO:
+ case DEPENDENCY_AUTO_EXTENSION:
/* no problem */
break;
case DEPENDENCY_INTERNAL:
@@ -788,6 +789,7 @@ findDependentObjects(const ObjectAddress *object,
subflags = DEPFLAG_NORMAL;
break;
case DEPENDENCY_AUTO:
+ case DEPENDENCY_AUTO_EXTENSION:
subflags = DEPFLAG_AUTO;
break;
case DEPENDENCY_INTERNAL:
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index cb3ba853f4..13244610db 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -1015,6 +1015,31 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
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.
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 5af0f2ffdf..7e39422ecd 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -390,6 +390,43 @@ ExecRenameStmt(RenameStmt *stmt)
}
}
+/*
+ * 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.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f4e4a91ba5..1e123d89cb 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3204,6 +3204,20 @@ _copyRenameStmt(const RenameStmt *from)
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)
{
@@ -4682,6 +4696,9 @@ copyObject(const void *from)
case T_RenameStmt:
retval = _copyRenameStmt(from);
break;
+ case T_AlterObjectDependsStmt:
+ retval = _copyAlterObjectDependsStmt(from);
+ break;
case T_AlterObjectSchemaStmt:
retval = _copyAlterObjectSchemaStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 854c062d32..6c0509602c 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1325,6 +1325,18 @@ _equalRenameStmt(const RenameStmt *a, const RenameStmt *b)
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)
{
@@ -3004,6 +3016,9 @@ equal(const void *a, const void *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;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 12733528eb..18ec5f03d8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -233,7 +233,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
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
@@ -578,7 +579,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
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
@@ -767,6 +768,7 @@ stmt :
| AlterForeignTableStmt
| AlterFunctionStmt
| AlterGroupStmt
+ | AlterObjectDependsStmt
| AlterObjectSchemaStmt
| AlterOwnerStmt
| AlterOperatorStmt
@@ -8025,6 +8027,55 @@ opt_set_data: SET DATA_P { $$ = 1; }
| /*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
@@ -13726,6 +13777,7 @@ unreserved_keyword:
| DELETE_P
| DELIMITER
| DELIMITERS
+ | DEPENDS
| DICTIONARY
| DISABLE_P
| DISCARD
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4d0aac979f..ac50c2a03d 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -147,6 +147,7 @@ check_xact_readonly(Node *parsetree)
case T_AlterFunctionStmt:
case T_AlterRoleStmt:
case T_AlterRoleSetStmt:
+ case T_AlterObjectDependsStmt:
case T_AlterObjectSchemaStmt:
case T_AlterOwnerStmt:
case T_AlterOperatorStmt:
@@ -836,6 +837,19 @@ standard_ProcessUtility(Node *parsetree,
}
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;
@@ -1472,6 +1486,12 @@ ProcessUtilitySlow(Node *parsetree,
address = ExecRenameStmt((RenameStmt *) parsetree);
break;
+ case T_AlterObjectDependsStmt:
+ address =
+ ExecAlterObjectDependsStmt((AlterObjectDependsStmt *) parsetree,
+ &secondaryObject);
+ break;
+
case T_AlterObjectSchemaStmt:
address =
ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree,
@@ -2192,6 +2212,10 @@ CreateCommandTag(Node *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;
@@ -2822,6 +2846,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
+ case T_AlterObjectDependsStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_AlterObjectSchemaStmt:
lev = LOGSTMT_DDL;
break;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 2be9aa9f49..440bfba7f9 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201604052
+#define CATALOG_VERSION_NO 201604053
#endif
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index d41abc4e48..09b36c5c78 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -55,6 +55,12 @@
* 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
@@ -70,6 +76,7 @@ typedef enum DependencyType
DEPENDENCY_AUTO = 'a',
DEPENDENCY_INTERNAL = 'i',
DEPENDENCY_EXTENSION = 'e',
+ DEPENDENCY_AUTO_EXTENSION = 'x',
DEPENDENCY_PIN = 'p'
} DependencyType;
diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h
index 640f7ffb1c..87aa41497d 100644
--- a/src/include/catalog/objectaddress.h
+++ b/src/include/catalog/objectaddress.h
@@ -44,6 +44,10 @@ extern ObjectAddress get_object_address(ObjectType objtype, List *objname,
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);
diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h
index cf92e3e859..e116bc73b7 100644
--- a/src/include/commands/alter.h
+++ b/src/include/commands/alter.h
@@ -21,6 +21,8 @@
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,
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 734df771eb..d888b41fd9 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -368,6 +368,7 @@ typedef enum NodeTag
T_DeclareCursorStmt,
T_CreateTableSpaceStmt,
T_DropTableSpaceStmt,
+ T_AlterObjectDependsStmt,
T_AlterObjectSchemaStmt,
T_AlterOwnerStmt,
T_AlterOperatorStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8b958b422c..714cf1550f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2535,6 +2535,20 @@ typedef struct RenameStmt
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
* ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 7de3404aa2..17ffef53a7 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -125,6 +125,7 @@ PG_KEYWORD("definer", DEFINER, UNRESERVED_KEYWORD)
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)
diff --git a/src/test/modules/test_extensions/Makefile b/src/test/modules/test_extensions/Makefile
index 5691357c07..283233782a 100644
--- a/src/test/modules/test_extensions/Makefile
+++ b/src/test/modules/test_extensions/Makefile
@@ -9,7 +9,7 @@ DATA = test_ext1--1.0.sql test_ext2--1.0.sql test_ext3--1.0.sql \
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
diff --git a/src/test/modules/test_extensions/expected/test_extdepend.out b/src/test/modules/test_extensions/expected/test_extdepend.out
new file mode 100644
index 0000000000..11e441ddd3
--- /dev/null
+++ b/src/test/modules/test_extensions/expected/test_extdepend.out
@@ -0,0 +1,152 @@
+--
+-- 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
diff --git a/src/test/modules/test_extensions/sql/test_extdepend.sql b/src/test/modules/test_extensions/sql/test_extdepend.sql
new file mode 100644
index 0000000000..cf44145dcb
--- /dev/null
+++ b/src/test/modules/test_extensions/sql/test_extdepend.sql
@@ -0,0 +1,73 @@
+--
+-- 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;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index c2511def9e..e293fc0bc8 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -67,6 +67,7 @@ AlterExtensionStmt
AlterFdwStmt
AlterForeignServerStmt
AlterFunctionStmt
+AlterObjectDependsStmt
AlterObjectSchemaStmt
AlterOpFamilyStmt
AlterOwnerStmt