Support ALTER THING .. DEPENDS ON EXTENSION

This introduces a new dependency type which marks an object as depending
on an extension, such that if the extension is dropped, the object
automatically goes away; and also, if the database is dumped, the object
is included in the dump output.  Currently the grammar supports this for
indexes, triggers, materialized views and functions only, although the
utility code is generic so adding support for more object types is a
matter of touching the parser rules only.

Author: Abhijit Menon-Sen
Reviewed-by: Alexander Korotkov, Álvaro Herrera
Discussion: http://www.postgresql.org/message-id/20160115062649.GA5068@toroid.org
This commit is contained in:
Alvaro Herrera 2016-04-05 18:38:54 -03:00
parent 41ea0c2376
commit f2fcad27d5
23 changed files with 524 additions and 5 deletions

View File

@ -2876,6 +2876,19 @@
</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>

View File

@ -29,6 +29,8 @@ ALTER FUNCTION <replaceable>name</replaceable> ( [ [ <replaceable class="paramet
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>
@ -148,6 +150,15 @@ ALTER FUNCTION <replaceable>name</replaceable> ( [ [ <replaceable class="paramet
</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>
@ -299,6 +310,15 @@ ALTER FUNCTION sqrt(integer) SET SCHEMA maths;
</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>

View File

@ -23,6 +23,7 @@ PostgreSQL documentation
<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> [, ... ] ]
@ -82,6 +83,16 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="PARAMETER">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>
@ -147,6 +158,15 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
</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>

View File

@ -23,6 +23,8 @@ PostgreSQL documentation
<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>
@ -67,6 +69,12 @@ 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
@ -99,6 +107,15 @@ ALTER MATERIALIZED VIEW ALL IN TABLESPACE <replaceable class="parameter">name</r
</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>

View File

@ -22,6 +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>
</synopsis>
</refsynopsisdiv>
@ -32,7 +33,9 @@ ALTER TRIGGER <replaceable class="PARAMETER">name</replaceable> ON <replaceable
<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>
@ -70,6 +73,15 @@ ALTER TRIGGER <replaceable class="PARAMETER">name</replaceable> ON <replaceable
</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>
@ -92,6 +104,12 @@ ALTER TRIGGER <replaceable class="PARAMETER">name</replaceable> ON <replaceable
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>

View File

@ -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:

View File

@ -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.

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201604052
#define CATALOG_VERSION_NO 201604053
#endif

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -368,6 +368,7 @@ typedef enum NodeTag
T_DeclareCursorStmt,
T_CreateTableSpaceStmt,
T_DropTableSpaceStmt,
T_AlterObjectDependsStmt,
T_AlterObjectSchemaStmt,
T_AlterOwnerStmt,
T_AlterOperatorStmt,

View File

@ -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
* ----------------------

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -67,6 +67,7 @@ AlterExtensionStmt
AlterFdwStmt
AlterForeignServerStmt
AlterFunctionStmt
AlterObjectDependsStmt
AlterObjectSchemaStmt
AlterOpFamilyStmt
AlterOwnerStmt