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