mirror of https://github.com/postgres/postgres
They don't actually do anything yet; that will get fixed in a follow-on commit. But this gets the basic infrastructure in place, including CREATE/ALTER/DROP EVENT TRIGGER; support for COMMENT, SECURITY LABEL, and ALTER EXTENSION .. ADD/DROP EVENT TRIGGER; pg_dump and psql support; and documentation for the anticipated initial feature set. Dimitri Fontaine, with review and a bunch of additional hacking by me. Thom Brown extensively reviewed earlier versions of this patch set, but there's not a whole lot of that code left in this commit, as it turns out.pull/3/head
parent
faf26bf117
commit
3855968f32
@ -0,0 +1,415 @@ |
|||||||
|
<!-- doc/src/sgml/event-trigger.sgml --> |
||||||
|
|
||||||
|
<chapter id="event-triggers"> |
||||||
|
<title>Event Triggers</title> |
||||||
|
|
||||||
|
<indexterm zone="event-triggers"> |
||||||
|
<primary>event trigger</primary> |
||||||
|
</indexterm> |
||||||
|
|
||||||
|
<para> |
||||||
|
To supplement the trigger mechanism discussed in <xref linkend="triggers">, |
||||||
|
<productname>PostgreSQL</> also provides event triggers. Unlike regular |
||||||
|
triggers, which are attached to a single table and capture only DML events, |
||||||
|
event triggers are global to a particular database and are capable of |
||||||
|
capturing DDL events. |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
Like regular triggers, event triggers can be written in any procedural |
||||||
|
language that includes event trigger support, or in C, but not in plain |
||||||
|
SQL. |
||||||
|
</para> |
||||||
|
|
||||||
|
<sect1 id="event-trigger-definition"> |
||||||
|
<title>Overview of Event Trigger Behavior</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
An event trigger fires whenever the event with which it is associated |
||||||
|
occurs in the database in which it is defined. Currently, the only |
||||||
|
supported event is <literal>ddl_command_start</>. Support for |
||||||
|
additional events may be added in future releases. |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
The <literal>ddl_command_start</> event occurs just before the |
||||||
|
execution of a <literal>CREATE</>, <literal>ALTER</>, or <literal>DROP</> |
||||||
|
commmand. As an exception, however, this event does not occur for |
||||||
|
DDL commands targeting shared objects - databases, roles, and tablespaces |
||||||
|
- or for command targeting event triggers themselves. The event trigger |
||||||
|
mechanism does not support these object types. |
||||||
|
<literal>ddl_command_start</> also occurs just before the execution of a |
||||||
|
<literal>SELECT INTO</literal> command, since this is equivalent to |
||||||
|
<literal>CREATE TABLE AS</literal>. |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
For a complete list of commands supported by the event trigger mechanism, |
||||||
|
see <xref linkend="event-trigger-matrix">. |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
In order to create an event trigger, you must first create a function with |
||||||
|
the special return type <literal>event_trigger</literal>. This function |
||||||
|
need not (and may not) return a value; the return type serves merely as |
||||||
|
a signal that the function is to be invoked as an event trigger. |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
If more than one event trigger is defined for a particular event, they will |
||||||
|
fire in alphabetical order by trigger name. |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
A trigger definition can also specify a <literal>WHEN</literal> |
||||||
|
condition so that, for example, a <literal>ddl_command_start</literal> |
||||||
|
trigger can be fired only for particular commands which the user wishes |
||||||
|
to intercept. A common use of such triggers is to restrict the range of |
||||||
|
DDL operations which users may perform. |
||||||
|
</para> |
||||||
|
</sect1> |
||||||
|
|
||||||
|
<sect1 id="event-trigger-matrix"> |
||||||
|
<title>Event Trigger Firing Matrix</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
<xref linkend="event-trigger-by-command-tag"> lists all commands |
||||||
|
for which event triggers are supported. |
||||||
|
</para> |
||||||
|
|
||||||
|
<table id="event-trigger-by-command-tag"> |
||||||
|
<title>Event Trigger Support by Command Tag</title> |
||||||
|
<tgroup cols="2"> |
||||||
|
<thead> |
||||||
|
<row> |
||||||
|
<entry>command tag</entry> |
||||||
|
<entry><literal>ddl_command_start</literal></entry> |
||||||
|
</row> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER AGGREGATE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER COLLATION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER CONVERSION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER DOMAIN</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER EXTENSION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER FOREIGN DATA WRAPPER</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER FOREIGN TABLE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER FUNCTION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER LANGUAGE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER OPERATOR</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER OPERATOR CLASS</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER OPERATOR FAMILY</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER SCHEMA</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER SEQUENCE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER SERVER</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER TABLE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER TEXT SEARCH CONFIGURATION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER TEXT SEARCH DICTIONARY</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER TEXT SEARCH PARSER</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER TEXT SEARCH TEMPLATE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER TRIGGER</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER TYPE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER USER MAPPING</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>ALTER VIEW</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE AGGREGATE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE CAST</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE COLLATION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE CONVERSION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE DOMAIN</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE EXTENSION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE FOREIGN DATA WRAPPER</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE FOREIGN TABLE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE FUNCTION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE INDEX</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE LANGUAGE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE OPERATOR</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE OPERATOR CLASS</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE OPERATOR FAMILY</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE RULE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE SCHEMA</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE SEQUENCE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE SERVER</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE TABLE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE TABLE AS</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE TEXT SEARCH CONFIGURATION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE TEXT SEARCH DICTIONARY</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE TEXT SEARCH PARSER</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE TEXT SEARCH TEMPLATE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE TRIGGER</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE TYPE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE USER MAPPING</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>CREATE VIEW</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP AGGREGATE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP CAST</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP COLLATION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP CONVERSION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP DOMAIN</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP EXTENSION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP FOREIGN DATA WRAPPER</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP FOREIGN TABLE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP FUNCTION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP INDEX</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP LANGUAGE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP OPERATOR</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP OPERATOR CLASS</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP OPERATOR FAMILY</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP RULE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP SCHEMA</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP SEQUENCE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP SERVER</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP TABLE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP TEXT SEARCH CONFIGURATION</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP TEXT SEARCH DICTIONARY</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP TEXT SEARCH PARSER</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP TEXT SEARCH TEMPLATE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP TRIGGER</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP TYPE</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP USER MAPPING</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>DROP VIEW</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
<row> |
||||||
|
<entry align="left"><literal>SELECT INTO</literal></entry> |
||||||
|
<entry align="center"><literal>X</literal></entry> |
||||||
|
</row> |
||||||
|
</tbody> |
||||||
|
</tgroup> |
||||||
|
</table> |
||||||
|
</sect1> |
||||||
|
|
||||||
|
</chapter> |
@ -0,0 +1,105 @@ |
|||||||
|
<!-- |
||||||
|
doc/src/sgml/ref/alter_event_trigger.sgml |
||||||
|
PostgreSQL documentation |
||||||
|
--> |
||||||
|
|
||||||
|
<refentry id="SQL-ALTEREVENTTRIGGER"> |
||||||
|
<refmeta> |
||||||
|
<refentrytitle>ALTER EVENT TRIGGER</refentrytitle> |
||||||
|
<manvolnum>7</manvolnum> |
||||||
|
<refmiscinfo>SQL - Language Statements</refmiscinfo> |
||||||
|
</refmeta> |
||||||
|
|
||||||
|
<refnamediv> |
||||||
|
<refname>ALTER EVENT TRIGGER</refname> |
||||||
|
<refpurpose>change the definition of an event trigger</refpurpose> |
||||||
|
</refnamediv> |
||||||
|
|
||||||
|
<indexterm zone="sql-altereventtrigger"> |
||||||
|
<primary>ALTER EVENT TRIGGER</primary> |
||||||
|
</indexterm> |
||||||
|
|
||||||
|
<refsynopsisdiv> |
||||||
|
<synopsis> |
||||||
|
ALTER EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable> DISABLE |
||||||
|
ALTER EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable> ENABLE [ REPLICA | ALWAYS ] |
||||||
|
ALTER EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable> |
||||||
|
ALTER EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable> |
||||||
|
</synopsis> |
||||||
|
</refsynopsisdiv> |
||||||
|
|
||||||
|
<refsect1> |
||||||
|
<title>Description</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
<command>ALTER EVENT TRIGGER</command> changes properties of an |
||||||
|
existing event trigger. |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
You must be superuser to alter an event trigger. |
||||||
|
</para> |
||||||
|
</refsect1> |
||||||
|
|
||||||
|
<refsect1> |
||||||
|
<title>Parameters</title> |
||||||
|
|
||||||
|
<variablelist> |
||||||
|
<varlistentry> |
||||||
|
<term><replaceable class="PARAMETER">name</replaceable></term> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
The name of an existing trigger to alter. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</varlistentry> |
||||||
|
|
||||||
|
<varlistentry> |
||||||
|
<term><replaceable class="PARAMETER">new_owner</replaceable></term> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
The user name of the new owner of the event trigger. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</varlistentry> |
||||||
|
|
||||||
|
<varlistentry> |
||||||
|
<term><replaceable class="PARAMETER">new_name</replaceable></term> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
The new name of the event trigger. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</varlistentry> |
||||||
|
|
||||||
|
<varlistentry> |
||||||
|
<term><literal>DISABLE</literal>/<literal>ENABLE [ REPLICA | ALWAYS ] TRIGGER</literal></term> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
These forms configure the firing of event triggers. A disabled trigger |
||||||
|
is still known to the system, but is not executed when its triggering |
||||||
|
event occurs. See also <xref linkend="guc-session-replication-role">. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</varlistentry> |
||||||
|
</variablelist> |
||||||
|
</refsect1> |
||||||
|
|
||||||
|
<refsect1 id="sql-alterventtrigger-compatibility"> |
||||||
|
<title>Compatibility</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
There is no <command>ALTER EVENT TRIGGER</command> statement in the |
||||||
|
SQL standard. |
||||||
|
</para> |
||||||
|
</refsect1> |
||||||
|
|
||||||
|
<refsect1> |
||||||
|
<title>See Also</title> |
||||||
|
|
||||||
|
<simplelist type="inline"> |
||||||
|
<member><xref linkend="sql-createeventtrigger"></member> |
||||||
|
<member><xref linkend="sql-dropeventtrigger"></member> |
||||||
|
</simplelist> |
||||||
|
</refsect1> |
||||||
|
</refentry> |
@ -0,0 +1,162 @@ |
|||||||
|
<!-- |
||||||
|
doc/src/sgml/ref/create_event_trigger.sgml |
||||||
|
PostgreSQL documentation |
||||||
|
--> |
||||||
|
|
||||||
|
<refentry id="SQL-CREATEEVENTTRIGGER"> |
||||||
|
<refmeta> |
||||||
|
<refentrytitle>CREATE EVENT TRIGGER</refentrytitle> |
||||||
|
<manvolnum>7</manvolnum> |
||||||
|
<refmiscinfo>SQL - Language Statements</refmiscinfo> |
||||||
|
</refmeta> |
||||||
|
|
||||||
|
<refnamediv> |
||||||
|
<refname>CREATE EVENT TRIGGER</refname> |
||||||
|
<refpurpose>define a new event trigger</refpurpose> |
||||||
|
</refnamediv> |
||||||
|
|
||||||
|
<indexterm zone="sql-createeventtrigger"> |
||||||
|
<primary>CREATE EVENT TRIGGER</primary> |
||||||
|
</indexterm> |
||||||
|
|
||||||
|
<refsynopsisdiv> |
||||||
|
<synopsis> |
||||||
|
CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable> |
||||||
|
ON <replaceable class="PARAMETER">event</replaceable> |
||||||
|
[ WHEN <replaceable class="PARAMETER">filter_variable</replaceable> IN (filter_value [ AND ... ] ) ] |
||||||
|
EXECUTE PROCEDURE <replaceable class="PARAMETER">function_name</replaceable>() |
||||||
|
</synopsis> |
||||||
|
</refsynopsisdiv> |
||||||
|
|
||||||
|
<refsect1> |
||||||
|
<title>Description</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
<command>CREATE EVENT TRIGGER</command> creates a new event trigger. |
||||||
|
Whenever the designated event occurs and the <literal>WHEN</> condition |
||||||
|
associated with the trigger, if any, is satisfied, the trigger function |
||||||
|
will be executed. For a general introduction to event triggers, see |
||||||
|
<xref linkend="event-triggers">. The user who creates an event trigger |
||||||
|
becomes its owner. |
||||||
|
</para> |
||||||
|
</refsect1> |
||||||
|
|
||||||
|
<refsect1> |
||||||
|
<title>Parameters</title> |
||||||
|
|
||||||
|
<variablelist> |
||||||
|
<varlistentry> |
||||||
|
<term><replaceable class="parameter">name</replaceable></term> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
The name to give the new trigger. This name must be unique within |
||||||
|
the database. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</varlistentry> |
||||||
|
|
||||||
|
<varlistentry> |
||||||
|
<term><replaceable class="parameter">event</replaceable></term> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
The name of the event that triggers a call to the given function. |
||||||
|
See <xref linkend="event-trigger-definition"> for more information |
||||||
|
on event names. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</varlistentry> |
||||||
|
|
||||||
|
<varlistentry> |
||||||
|
<term><replaceable class="parameter">filter_variable</replaceable></term> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
The name of a variable used to filter events. This makes it possible |
||||||
|
to restrict the firing of the trigger to a subset of the cases in which |
||||||
|
it is supported. Currently the only supported |
||||||
|
<replaceable class="parameter">filter_variable</replaceable> |
||||||
|
is <literal>TAG</literal>. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</varlistentry> |
||||||
|
|
||||||
|
<varlistentry> |
||||||
|
<term><replaceable class="parameter">filter_value</replaceable></term> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
A list of values for the |
||||||
|
associated <replaceable class="parameter">filter_variable</replaceable> |
||||||
|
for which the trigger should fire. For <literal>TAG</>, this means a |
||||||
|
list of command tags (e.g. <literal>'DROP FUNCTION'</>). |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</varlistentry> |
||||||
|
|
||||||
|
<varlistentry> |
||||||
|
<term><replaceable class="parameter">function_name</replaceable></term> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
A user-supplied function that is declared as taking no argument and |
||||||
|
returning type <literal>event_trigger</literal>. |
||||||
|
</para> |
||||||
|
<para> |
||||||
|
If your event trigger is implemented in <literal>C</literal> then it |
||||||
|
will be called with an argument, of |
||||||
|
type <literal>internal</literal>, which is a pointer to |
||||||
|
the <literal>Node *</literal> parse tree. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</varlistentry> |
||||||
|
|
||||||
|
</variablelist> |
||||||
|
</refsect1> |
||||||
|
|
||||||
|
<refsect1 id="sql-createeventtrigger-notes"> |
||||||
|
<title>Notes</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
To create a trigger on a event, the user must be superuser. |
||||||
|
</para> |
||||||
|
</refsect1> |
||||||
|
|
||||||
|
<refsect1 id="sql-createeventtrigger-examples"> |
||||||
|
<title>Examples</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
Forbid the execution of any <link linkend="ddl">ddl</link> command: |
||||||
|
|
||||||
|
<programlisting> |
||||||
|
CREATE OR REPLACE FUNCTION abort_any_command() |
||||||
|
RETURNS event_trigger |
||||||
|
LANGUAGE plpgsql |
||||||
|
AS $$ |
||||||
|
BEGIN |
||||||
|
RAISE EXCEPTION 'command % is disabled', tg_tag; |
||||||
|
END; |
||||||
|
$$; |
||||||
|
|
||||||
|
CREATE EVENT TRIGGER abort_ddl ON ddl_command_start |
||||||
|
EXECUTE PROCEDURE abort_any_command(); |
||||||
|
</programlisting> |
||||||
|
</para> |
||||||
|
</refsect1> |
||||||
|
|
||||||
|
<refsect1 id="sql-createeventtrigger-compatibility"> |
||||||
|
<title>Compatibility</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
There is no <command>CREATE EVENT TRIGGER</command> statement in the |
||||||
|
SQL standard. |
||||||
|
</para> |
||||||
|
|
||||||
|
</refsect1> |
||||||
|
|
||||||
|
<refsect1> |
||||||
|
<title>See Also</title> |
||||||
|
|
||||||
|
<simplelist type="inline"> |
||||||
|
<member><xref linkend="sql-createfunction"></member> |
||||||
|
<member><xref linkend="sql-altereventtrigger"></member> |
||||||
|
<member><xref linkend="sql-dropeventtrigger"></member> |
||||||
|
</simplelist> |
||||||
|
</refsect1> |
||||||
|
</refentry> |
@ -0,0 +1,113 @@ |
|||||||
|
<!-- |
||||||
|
doc/src/sgml/ref/drop_event_trigger.sgml |
||||||
|
PostgreSQL documentation |
||||||
|
--> |
||||||
|
|
||||||
|
<refentry id="SQL-DROPEVENTTRIGGER"> |
||||||
|
<refmeta> |
||||||
|
<refentrytitle>DROP EVENT TRIGGER</refentrytitle> |
||||||
|
<manvolnum>7</manvolnum> |
||||||
|
<refmiscinfo>SQL - Language Statements</refmiscinfo> |
||||||
|
</refmeta> |
||||||
|
|
||||||
|
<refnamediv> |
||||||
|
<refname>DROP EVENT TRIGGER</refname> |
||||||
|
<refpurpose>remove an event trigger</refpurpose> |
||||||
|
</refnamediv> |
||||||
|
|
||||||
|
<indexterm zone="sql-dropeventtrigger"> |
||||||
|
<primary>DROP EVENT TRIGGER</primary> |
||||||
|
</indexterm> |
||||||
|
|
||||||
|
<refsynopsisdiv> |
||||||
|
<synopsis> |
||||||
|
DROP EVENT TRIGGER [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> [ CASCADE | RESTRICT ] |
||||||
|
</synopsis> |
||||||
|
</refsynopsisdiv> |
||||||
|
|
||||||
|
<refsect1> |
||||||
|
<title>Description</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
<command>DROP EVENT TRIGGER</command> removes an existing event trigger. |
||||||
|
To execute this command, the current user must be the owner of the event |
||||||
|
trigger. |
||||||
|
</para> |
||||||
|
</refsect1> |
||||||
|
|
||||||
|
<refsect1> |
||||||
|
<title>Parameters</title> |
||||||
|
|
||||||
|
<variablelist> |
||||||
|
|
||||||
|
<varlistentry> |
||||||
|
<term><literal>IF EXISTS</literal></term> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
Do not throw an error if the event trigger does not exist. A notice |
||||||
|
is issued in this case. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</varlistentry> |
||||||
|
|
||||||
|
<varlistentry> |
||||||
|
<term><replaceable class="PARAMETER">name</replaceable></term> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
The name of the event trigger to remove. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</varlistentry> |
||||||
|
|
||||||
|
<varlistentry> |
||||||
|
<term><literal>CASCADE</literal></term> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
Automatically drop objects that depend on the trigger. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</varlistentry> |
||||||
|
|
||||||
|
<varlistentry> |
||||||
|
<term><literal>RESTRICT</literal></term> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
Refuse to drop the trigger if any objects depend on it. This is |
||||||
|
the default. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</varlistentry> |
||||||
|
</variablelist> |
||||||
|
</refsect1> |
||||||
|
|
||||||
|
<refsect1 id="sql-dropeventtrigger-examples"> |
||||||
|
<title>Examples</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
Destroy the trigger <literal>snitch</literal>: |
||||||
|
|
||||||
|
<programlisting> |
||||||
|
DROP EVENT TRIGGER snitch; |
||||||
|
</programlisting></para> |
||||||
|
</refsect1> |
||||||
|
|
||||||
|
<refsect1 id="sql-dropeventtrigger-compatibility"> |
||||||
|
<title>Compatibility</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
There is no <command>DROP EVENT TRIGGER</command> statement in the |
||||||
|
SQL standard. |
||||||
|
</para> |
||||||
|
|
||||||
|
</refsect1> |
||||||
|
|
||||||
|
<refsect1> |
||||||
|
<title>See Also</title> |
||||||
|
|
||||||
|
<simplelist type="inline"> |
||||||
|
<member><xref linkend="sql-createeventtrigger"></member> |
||||||
|
<member><xref linkend="sql-altereventtrigger"></member> |
||||||
|
</simplelist> |
||||||
|
</refsect1> |
||||||
|
|
||||||
|
</refentry> |
@ -0,0 +1,539 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* event_trigger.c |
||||||
|
* PostgreSQL EVENT TRIGGER support code. |
||||||
|
* |
||||||
|
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group |
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California |
||||||
|
* |
||||||
|
* IDENTIFICATION |
||||||
|
* src/backend/commands/event_trigger.c |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
#include "postgres.h" |
||||||
|
|
||||||
|
#include "catalog/dependency.h" |
||||||
|
#include "catalog/indexing.h" |
||||||
|
#include "catalog/objectaccess.h" |
||||||
|
#include "catalog/pg_event_trigger.h" |
||||||
|
#include "catalog/pg_proc.h" |
||||||
|
#include "catalog/pg_trigger.h" |
||||||
|
#include "catalog/pg_type.h" |
||||||
|
#include "commands/dbcommands.h" |
||||||
|
#include "commands/event_trigger.h" |
||||||
|
#include "commands/trigger.h" |
||||||
|
#include "parser/parse_func.h" |
||||||
|
#include "pgstat.h" |
||||||
|
#include "miscadmin.h" |
||||||
|
#include "utils/acl.h" |
||||||
|
#include "utils/builtins.h" |
||||||
|
#include "utils/fmgroids.h" |
||||||
|
#include "utils/lsyscache.h" |
||||||
|
#include "utils/memutils.h" |
||||||
|
#include "utils/rel.h" |
||||||
|
#include "utils/tqual.h" |
||||||
|
#include "utils/syscache.h" |
||||||
|
#include "tcop/utility.h" |
||||||
|
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
const char *obtypename; |
||||||
|
ObjectType obtype; |
||||||
|
bool supported; |
||||||
|
} event_trigger_support_data; |
||||||
|
|
||||||
|
static event_trigger_support_data event_trigger_support[] = { |
||||||
|
{ "AGGREGATE", OBJECT_AGGREGATE, true }, |
||||||
|
{ "CAST", OBJECT_CAST, true }, |
||||||
|
{ "CONSTRAINT", OBJECT_CONSTRAINT, true }, |
||||||
|
{ "COLLATION", OBJECT_COLLATION, true }, |
||||||
|
{ "CONVERSION", OBJECT_CONVERSION, true }, |
||||||
|
{ "DATABASE", OBJECT_DATABASE, false }, |
||||||
|
{ "DOMAIN", OBJECT_DOMAIN, true }, |
||||||
|
{ "EXTENSION", OBJECT_EXTENSION, true }, |
||||||
|
{ "EVENT TRIGGER", OBJECT_EVENT_TRIGGER, false }, |
||||||
|
{ "FOREIGN DATA WRAPPER", OBJECT_FDW, true }, |
||||||
|
{ "FOREIGN SERVER", OBJECT_FOREIGN_SERVER, true }, |
||||||
|
{ "FOREIGN TABLE", OBJECT_FOREIGN_TABLE, true }, |
||||||
|
{ "FUNCTION", OBJECT_FUNCTION, true }, |
||||||
|
{ "INDEX", OBJECT_INDEX, true }, |
||||||
|
{ "LANGUAGE", OBJECT_LANGUAGE, true }, |
||||||
|
{ "OPERATOR", OBJECT_OPERATOR, true }, |
||||||
|
{ "OPERATOR CLASS", OBJECT_OPCLASS, true }, |
||||||
|
{ "OPERATOR FAMILY", OBJECT_OPFAMILY, true }, |
||||||
|
{ "ROLE", OBJECT_ROLE, false }, |
||||||
|
{ "RULE", OBJECT_RULE, true }, |
||||||
|
{ "SCHEMA", OBJECT_SCHEMA, true }, |
||||||
|
{ "SEQUENCE", OBJECT_SEQUENCE, true }, |
||||||
|
{ "TABLE", OBJECT_TABLE, true }, |
||||||
|
{ "TABLESPACE", OBJECT_TABLESPACE, false}, |
||||||
|
{ "TRIGGER", OBJECT_TRIGGER, true }, |
||||||
|
{ "TEXT SEARCH CONFIGURATION", OBJECT_TSCONFIGURATION, true }, |
||||||
|
{ "TEXT SEARCH DICTIONARY", OBJECT_TSDICTIONARY, true }, |
||||||
|
{ "TEXT SEARCH PARSER", OBJECT_TSPARSER, true }, |
||||||
|
{ "TEXT SEARCH TEMPLATE", OBJECT_TSTEMPLATE, true }, |
||||||
|
{ "TYPE", OBJECT_TYPE, true }, |
||||||
|
{ "VIEW", OBJECT_VIEW, true }, |
||||||
|
{ NULL, (ObjectType) 0, false } |
||||||
|
}; |
||||||
|
|
||||||
|
static void AlterEventTriggerOwner_internal(Relation rel, |
||||||
|
HeapTuple tup, |
||||||
|
Oid newOwnerId); |
||||||
|
static void error_duplicate_filter_variable(const char *defname); |
||||||
|
static void error_unrecognized_filter_value(const char *var, const char *val); |
||||||
|
static Datum filter_list_to_array(List *filterlist); |
||||||
|
static void insert_event_trigger_tuple(char *trigname, char *eventname, |
||||||
|
Oid evtOwner, Oid funcoid, List *tags); |
||||||
|
static void validate_ddl_tags(const char *filtervar, List *taglist); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Create an event trigger. |
||||||
|
*/ |
||||||
|
void |
||||||
|
CreateEventTrigger(CreateEventTrigStmt *stmt) |
||||||
|
{ |
||||||
|
HeapTuple tuple; |
||||||
|
Oid funcoid; |
||||||
|
Oid funcrettype; |
||||||
|
Oid evtowner = GetUserId(); |
||||||
|
ListCell *lc; |
||||||
|
List *tags = NULL; |
||||||
|
|
||||||
|
/*
|
||||||
|
* It would be nice to allow database owners or even regular users to do |
||||||
|
* this, but there are obvious privilege escalation risks which would have |
||||||
|
* to somehow be plugged first. |
||||||
|
*/ |
||||||
|
if (!superuser()) |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
||||||
|
errmsg("permission denied to create event trigger \"%s\"", |
||||||
|
stmt->trigname), |
||||||
|
errhint("Must be superuser to create an event trigger."))); |
||||||
|
|
||||||
|
/* Validate event name. */ |
||||||
|
if (strcmp(stmt->eventname, "ddl_command_start") != 0) |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR), |
||||||
|
errmsg("unrecognized event name \"%s\"", |
||||||
|
stmt->eventname))); |
||||||
|
|
||||||
|
/* Validate filter conditions. */ |
||||||
|
foreach (lc, stmt->whenclause) |
||||||
|
{ |
||||||
|
DefElem *def = (DefElem *) lfirst(lc); |
||||||
|
|
||||||
|
if (strcmp(def->defname, "tag") == 0) |
||||||
|
{ |
||||||
|
if (tags != NULL) |
||||||
|
error_duplicate_filter_variable(def->defname); |
||||||
|
tags = (List *) def->arg; |
||||||
|
} |
||||||
|
else |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR), |
||||||
|
errmsg("unrecognized filter variable \"%s\"", def->defname))); |
||||||
|
} |
||||||
|
|
||||||
|
/* Validate tag list, if any. */ |
||||||
|
if (strcmp(stmt->eventname, "ddl_command_start") == 0 && tags != NULL) |
||||||
|
validate_ddl_tags("tag", tags); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Give user a nice error message if an event trigger of the same name |
||||||
|
* already exists. |
||||||
|
*/ |
||||||
|
tuple = SearchSysCache1(EVENTTRIGGERNAME, CStringGetDatum(stmt->trigname)); |
||||||
|
if (HeapTupleIsValid(tuple)) |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_DUPLICATE_OBJECT), |
||||||
|
errmsg("event trigger \"%s\" already exists", |
||||||
|
stmt->trigname))); |
||||||
|
|
||||||
|
/* Find and validate the trigger function. */ |
||||||
|
funcoid = LookupFuncName(stmt->funcname, 0, NULL, false); |
||||||
|
funcrettype = get_func_rettype(funcoid); |
||||||
|
if (funcrettype != EVTTRIGGEROID) |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), |
||||||
|
errmsg("function \"%s\" must return type \"event_trigger\"", |
||||||
|
NameListToString(stmt->funcname)))); |
||||||
|
|
||||||
|
/* Insert catalog entries. */ |
||||||
|
insert_event_trigger_tuple(stmt->trigname, stmt->eventname, |
||||||
|
evtowner, funcoid, tags); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Validate DDL command tags. |
||||||
|
*/ |
||||||
|
static void |
||||||
|
validate_ddl_tags(const char *filtervar, List *taglist) |
||||||
|
{ |
||||||
|
ListCell *lc; |
||||||
|
|
||||||
|
foreach (lc, taglist) |
||||||
|
{ |
||||||
|
const char *tag = strVal(lfirst(lc)); |
||||||
|
const char *obtypename = NULL; |
||||||
|
event_trigger_support_data *etsd; |
||||||
|
|
||||||
|
/*
|
||||||
|
* As a special case, SELECT INTO is considered DDL, since it creates |
||||||
|
* a table. |
||||||
|
*/ |
||||||
|
if (strcmp(tag, "SELECT INTO") == 0) |
||||||
|
continue; |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Otherwise, it should be CREATE, ALTER, or DROP. |
||||||
|
*/ |
||||||
|
if (pg_strncasecmp(tag, "CREATE ", 7) == 0) |
||||||
|
obtypename = tag + 7; |
||||||
|
else if (pg_strncasecmp(tag, "ALTER ", 6) == 0) |
||||||
|
obtypename = tag + 6; |
||||||
|
else if (pg_strncasecmp(tag, "DROP ", 5) == 0) |
||||||
|
obtypename = tag + 5; |
||||||
|
if (obtypename == NULL) |
||||||
|
error_unrecognized_filter_value(filtervar, tag); |
||||||
|
|
||||||
|
/*
|
||||||
|
* ...and the object type should be something recognizable. |
||||||
|
*/ |
||||||
|
for (etsd = event_trigger_support; etsd->obtypename != NULL; etsd++) |
||||||
|
if (pg_strcasecmp(etsd->obtypename, obtypename) == 0) |
||||||
|
break; |
||||||
|
if (etsd->obtypename == NULL) |
||||||
|
error_unrecognized_filter_value(filtervar, tag); |
||||||
|
if (!etsd->supported) |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
||||||
|
/* translator: %s represents an SQL statement name */ |
||||||
|
errmsg("event triggers are not supported for \"%s\"", |
||||||
|
tag))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Complain about a duplicate filter variable. |
||||||
|
*/ |
||||||
|
static void |
||||||
|
error_duplicate_filter_variable(const char *defname) |
||||||
|
{ |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR), |
||||||
|
errmsg("filter variable \"%s\" specified more than once", |
||||||
|
defname))); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Complain about an invalid filter value. |
||||||
|
*/ |
||||||
|
static void |
||||||
|
error_unrecognized_filter_value(const char *var, const char *val) |
||||||
|
{ |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR), |
||||||
|
errmsg("filter value \"%s\" not recognized for filter variable \"%s\"", |
||||||
|
val, var))); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert the new pg_event_trigger row and record dependencies. |
||||||
|
*/ |
||||||
|
static void |
||||||
|
insert_event_trigger_tuple(char *trigname, char *eventname, Oid evtOwner, |
||||||
|
Oid funcoid, List *taglist) |
||||||
|
{ |
||||||
|
Relation tgrel; |
||||||
|
Oid trigoid; |
||||||
|
HeapTuple tuple; |
||||||
|
Datum values[Natts_pg_trigger]; |
||||||
|
bool nulls[Natts_pg_trigger]; |
||||||
|
ObjectAddress myself, referenced; |
||||||
|
|
||||||
|
/* Open pg_event_trigger. */ |
||||||
|
tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock); |
||||||
|
|
||||||
|
/* Build the new pg_trigger tuple. */ |
||||||
|
memset(nulls, false, sizeof(nulls)); |
||||||
|
values[Anum_pg_event_trigger_evtname - 1] = NameGetDatum(trigname); |
||||||
|
values[Anum_pg_event_trigger_evtevent - 1] = NameGetDatum(eventname); |
||||||
|
values[Anum_pg_event_trigger_evtowner - 1] = ObjectIdGetDatum(evtOwner); |
||||||
|
values[Anum_pg_event_trigger_evtfoid - 1] = ObjectIdGetDatum(funcoid); |
||||||
|
values[Anum_pg_event_trigger_evtenabled - 1] = |
||||||
|
CharGetDatum(TRIGGER_FIRES_ON_ORIGIN); |
||||||
|
if (taglist == NIL) |
||||||
|
nulls[Anum_pg_event_trigger_evttags - 1] = true; |
||||||
|
else |
||||||
|
values[Anum_pg_event_trigger_evttags - 1] = |
||||||
|
filter_list_to_array(taglist); |
||||||
|
|
||||||
|
/* Insert heap tuple. */ |
||||||
|
tuple = heap_form_tuple(tgrel->rd_att, values, nulls); |
||||||
|
trigoid = simple_heap_insert(tgrel, tuple); |
||||||
|
CatalogUpdateIndexes(tgrel, tuple); |
||||||
|
heap_freetuple(tuple); |
||||||
|
|
||||||
|
/* Depend on owner. */ |
||||||
|
recordDependencyOnOwner(EventTriggerRelationId, trigoid, evtOwner); |
||||||
|
|
||||||
|
/* Depend on event trigger function. */ |
||||||
|
myself.classId = EventTriggerRelationId; |
||||||
|
myself.objectId = trigoid; |
||||||
|
myself.objectSubId = 0; |
||||||
|
referenced.classId = ProcedureRelationId; |
||||||
|
referenced.objectId = funcoid; |
||||||
|
referenced.objectSubId = 0; |
||||||
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
||||||
|
|
||||||
|
/* Post creation hook for new operator family */ |
||||||
|
InvokeObjectAccessHook(OAT_POST_CREATE, |
||||||
|
EventTriggerRelationId, trigoid, 0, NULL); |
||||||
|
|
||||||
|
/* Close pg_event_trigger. */ |
||||||
|
heap_close(tgrel, RowExclusiveLock); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* In the parser, a clause like WHEN tag IN ('cmd1', 'cmd2') is represented |
||||||
|
* by a DefElem whose value is a List of String nodes; in the catalog, we |
||||||
|
* store the list of strings as a text array. This function transforms the |
||||||
|
* former representation into the latter one. |
||||||
|
* |
||||||
|
* For cleanliness, we store command tags in the catalog as text. It's |
||||||
|
* possible (although not currently anticipated) that we might have |
||||||
|
* a case-sensitive filter variable in the future, in which case this would |
||||||
|
* need some further adjustment. |
||||||
|
*/ |
||||||
|
static Datum |
||||||
|
filter_list_to_array(List *filterlist) |
||||||
|
{ |
||||||
|
ListCell *lc; |
||||||
|
Datum *data; |
||||||
|
int i = 0, |
||||||
|
l = list_length(filterlist); |
||||||
|
|
||||||
|
data = (Datum *) palloc(l * sizeof(Datum)); |
||||||
|
|
||||||
|
foreach(lc, filterlist) |
||||||
|
{ |
||||||
|
const char *value = strVal(lfirst(lc)); |
||||||
|
char *result, |
||||||
|
*p; |
||||||
|
|
||||||
|
result = pstrdup(value); |
||||||
|
for (p = result; *p; p++) |
||||||
|
*p = pg_ascii_toupper((unsigned char) *p); |
||||||
|
data[i++] = PointerGetDatum(cstring_to_text(result)); |
||||||
|
pfree(result); |
||||||
|
} |
||||||
|
|
||||||
|
return PointerGetDatum(construct_array(data, l, TEXTOID, -1, false, 'i')); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Guts of event trigger deletion. |
||||||
|
*/ |
||||||
|
void |
||||||
|
RemoveEventTriggerById(Oid trigOid) |
||||||
|
{ |
||||||
|
Relation tgrel; |
||||||
|
HeapTuple tup; |
||||||
|
|
||||||
|
tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock); |
||||||
|
|
||||||
|
tup = SearchSysCache1(EVENTTRIGGEROID, ObjectIdGetDatum(trigOid)); |
||||||
|
if (!HeapTupleIsValid(tup)) |
||||||
|
elog(ERROR, "cache lookup failed for event trigger %u", trigOid); |
||||||
|
|
||||||
|
simple_heap_delete(tgrel, &tup->t_self); |
||||||
|
|
||||||
|
ReleaseSysCache(tup); |
||||||
|
|
||||||
|
heap_close(tgrel, RowExclusiveLock); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA |
||||||
|
*/ |
||||||
|
void |
||||||
|
AlterEventTrigger(AlterEventTrigStmt *stmt) |
||||||
|
{ |
||||||
|
Relation tgrel; |
||||||
|
HeapTuple tup; |
||||||
|
Form_pg_event_trigger evtForm; |
||||||
|
char tgenabled = stmt->tgenabled; |
||||||
|
|
||||||
|
tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock); |
||||||
|
|
||||||
|
tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, |
||||||
|
CStringGetDatum(stmt->trigname)); |
||||||
|
if (!HeapTupleIsValid(tup)) |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT), |
||||||
|
errmsg("event trigger \"%s\" does not exist", |
||||||
|
stmt->trigname))); |
||||||
|
if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId())) |
||||||
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER, |
||||||
|
stmt->trigname); |
||||||
|
|
||||||
|
/* tuple is a copy, so we can modify it below */ |
||||||
|
evtForm = (Form_pg_event_trigger) GETSTRUCT(tup); |
||||||
|
evtForm->evtenabled = tgenabled; |
||||||
|
|
||||||
|
simple_heap_update(tgrel, &tup->t_self, tup); |
||||||
|
CatalogUpdateIndexes(tgrel, tup); |
||||||
|
|
||||||
|
/* clean up */ |
||||||
|
heap_freetuple(tup); |
||||||
|
heap_close(tgrel, RowExclusiveLock); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rename event trigger |
||||||
|
*/ |
||||||
|
void |
||||||
|
RenameEventTrigger(const char *trigname, const char *newname) |
||||||
|
{ |
||||||
|
HeapTuple tup; |
||||||
|
Relation rel; |
||||||
|
Form_pg_event_trigger evtForm; |
||||||
|
|
||||||
|
rel = heap_open(EventTriggerRelationId, RowExclusiveLock); |
||||||
|
|
||||||
|
/* newname must be available */ |
||||||
|
if (SearchSysCacheExists1(EVENTTRIGGERNAME, CStringGetDatum(newname))) |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_DUPLICATE_OBJECT), |
||||||
|
errmsg("event trigger \"%s\" already exists", newname))); |
||||||
|
|
||||||
|
/* trigname must exists */ |
||||||
|
tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(trigname)); |
||||||
|
if (!HeapTupleIsValid(tup)) |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT), |
||||||
|
errmsg("event trigger \"%s\" does not exist", trigname))); |
||||||
|
if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId())) |
||||||
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER, |
||||||
|
trigname); |
||||||
|
|
||||||
|
evtForm = (Form_pg_event_trigger) GETSTRUCT(tup); |
||||||
|
|
||||||
|
/* tuple is a copy, so we can rename it now */ |
||||||
|
namestrcpy(&(evtForm->evtname), newname); |
||||||
|
simple_heap_update(rel, &tup->t_self, tup); |
||||||
|
CatalogUpdateIndexes(rel, tup); |
||||||
|
|
||||||
|
heap_freetuple(tup); |
||||||
|
heap_close(rel, RowExclusiveLock); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Change event trigger's owner -- by name |
||||||
|
*/ |
||||||
|
void |
||||||
|
AlterEventTriggerOwner(const char *name, Oid newOwnerId) |
||||||
|
{ |
||||||
|
HeapTuple tup; |
||||||
|
Relation rel; |
||||||
|
|
||||||
|
rel = heap_open(EventTriggerRelationId, RowExclusiveLock); |
||||||
|
|
||||||
|
tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(name)); |
||||||
|
|
||||||
|
if (!HeapTupleIsValid(tup)) |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT), |
||||||
|
errmsg("event trigger \"%s\" does not exist", name))); |
||||||
|
|
||||||
|
AlterEventTriggerOwner_internal(rel, tup, newOwnerId); |
||||||
|
|
||||||
|
heap_freetuple(tup); |
||||||
|
|
||||||
|
heap_close(rel, RowExclusiveLock); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Change extension owner, by OID |
||||||
|
*/ |
||||||
|
void |
||||||
|
AlterEventTriggerOwner_oid(Oid trigOid, Oid newOwnerId) |
||||||
|
{ |
||||||
|
HeapTuple tup; |
||||||
|
Relation rel; |
||||||
|
|
||||||
|
rel = heap_open(EventTriggerRelationId, RowExclusiveLock); |
||||||
|
|
||||||
|
tup = SearchSysCacheCopy1(EVENTTRIGGEROID, ObjectIdGetDatum(trigOid)); |
||||||
|
|
||||||
|
if (!HeapTupleIsValid(tup)) |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT), |
||||||
|
errmsg("event trigger with OID %u does not exist", trigOid))); |
||||||
|
|
||||||
|
AlterEventTriggerOwner_internal(rel, tup, newOwnerId); |
||||||
|
|
||||||
|
heap_freetuple(tup); |
||||||
|
|
||||||
|
heap_close(rel, RowExclusiveLock); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal workhorse for changing an event trigger's owner |
||||||
|
*/ |
||||||
|
static void |
||||||
|
AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) |
||||||
|
{ |
||||||
|
Form_pg_event_trigger form; |
||||||
|
|
||||||
|
form = (Form_pg_event_trigger) GETSTRUCT(tup); |
||||||
|
|
||||||
|
if (form->evtowner == newOwnerId) |
||||||
|
return; |
||||||
|
|
||||||
|
if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId())) |
||||||
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER, |
||||||
|
NameStr(form->evtname)); |
||||||
|
|
||||||
|
/* New owner must be a superuser */ |
||||||
|
if (!superuser_arg(newOwnerId)) |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
||||||
|
errmsg("permission denied to change owner of event trigger \"%s\"", |
||||||
|
NameStr(form->evtname)), |
||||||
|
errhint("The owner of an event trigger must be a superuser."))); |
||||||
|
|
||||||
|
form->evtowner = newOwnerId; |
||||||
|
simple_heap_update(rel, &tup->t_self, tup); |
||||||
|
CatalogUpdateIndexes(rel, tup); |
||||||
|
|
||||||
|
/* Update owner dependency reference */ |
||||||
|
changeDependencyOnOwner(EventTriggerRelationId, |
||||||
|
HeapTupleGetOid(tup), |
||||||
|
newOwnerId); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* get_event_trigger_oid - Look up an event trigger by name to find its OID. |
||||||
|
* |
||||||
|
* If missing_ok is false, throw an error if trigger not found. If |
||||||
|
* true, just return InvalidOid. |
||||||
|
*/ |
||||||
|
Oid |
||||||
|
get_event_trigger_oid(const char *trigname, bool missing_ok) |
||||||
|
{ |
||||||
|
Oid oid; |
||||||
|
|
||||||
|
oid = GetSysCacheOid1(EVENTTRIGGERNAME, CStringGetDatum(trigname)); |
||||||
|
if (!OidIsValid(oid) && !missing_ok) |
||||||
|
ereport(ERROR, |
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT), |
||||||
|
errmsg("event trigger \"%s\" does not exist", trigname))); |
||||||
|
return oid; |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* pg_event_trigger.h |
||||||
|
* definition of the system "event trigger" relation (pg_event_trigger) |
||||||
|
* along with the relation's initial contents. |
||||||
|
* |
||||||
|
* |
||||||
|
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group |
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California |
||||||
|
* |
||||||
|
* src/include/catalog/pg_event_trigger.h |
||||||
|
* |
||||||
|
* NOTES |
||||||
|
* the genbki.pl script reads this file and generates .bki |
||||||
|
* information from the DATA() statements. |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
#ifndef PG_EVENT_TRIGGER_H |
||||||
|
#define PG_EVENT_TRIGGER_H |
||||||
|
|
||||||
|
#include "catalog/genbki.h" |
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* pg_event_trigger definition. cpp turns this into |
||||||
|
* typedef struct FormData_pg_event_trigger |
||||||
|
* ---------------- |
||||||
|
*/ |
||||||
|
#define EventTriggerRelationId 3466 |
||||||
|
|
||||||
|
CATALOG(pg_event_trigger,3466) |
||||||
|
{ |
||||||
|
NameData evtname; /* trigger's name */ |
||||||
|
NameData evtevent; /* trigger's event */ |
||||||
|
Oid evtowner; /* trigger's owner */ |
||||||
|
Oid evtfoid; /* OID of function to be called */ |
||||||
|
char evtenabled; /* trigger's firing configuration WRT
|
||||||
|
* session_replication_role */ |
||||||
|
#ifdef CATALOG_VARLEN |
||||||
|
text evttags[1]; /* command TAGs this event trigger targets */ |
||||||
|
#endif |
||||||
|
} FormData_pg_event_trigger; |
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* Form_pg_event_trigger corresponds to a pointer to a tuple with |
||||||
|
* the format of pg_event_trigger relation. |
||||||
|
* ---------------- |
||||||
|
*/ |
||||||
|
typedef FormData_pg_event_trigger *Form_pg_event_trigger; |
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* compiler constants for pg_event_trigger |
||||||
|
* ---------------- |
||||||
|
*/ |
||||||
|
#define Natts_pg_event_trigger 6 |
||||||
|
#define Anum_pg_event_trigger_evtname 1 |
||||||
|
#define Anum_pg_event_trigger_evtevent 2 |
||||||
|
#define Anum_pg_event_trigger_evtowner 3 |
||||||
|
#define Anum_pg_event_trigger_evtfoid 4 |
||||||
|
#define Anum_pg_event_trigger_evtenabled 5 |
||||||
|
#define Anum_pg_event_trigger_evttags 6 |
||||||
|
|
||||||
|
#endif /* PG_EVENT_TRIGGER_H */ |
@ -0,0 +1,28 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* event_trigger.h |
||||||
|
* Declarations for command trigger handling. |
||||||
|
* |
||||||
|
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group |
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California |
||||||
|
* |
||||||
|
* src/include/commands/event_trigger.h |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
#ifndef EVENT_TRIGGER_H |
||||||
|
#define EVENT_TRIGGER_H |
||||||
|
|
||||||
|
#include "catalog/pg_event_trigger.h" |
||||||
|
#include "nodes/parsenodes.h" |
||||||
|
|
||||||
|
extern void CreateEventTrigger(CreateEventTrigStmt *stmt); |
||||||
|
extern void RemoveEventTriggerById(Oid ctrigOid); |
||||||
|
extern Oid get_event_trigger_oid(const char *trigname, bool missing_ok); |
||||||
|
|
||||||
|
extern void AlterEventTrigger(AlterEventTrigStmt *stmt); |
||||||
|
extern void RenameEventTrigger(const char* trigname, const char *newname); |
||||||
|
extern void AlterEventTriggerOwner(const char *name, Oid newOwnerId); |
||||||
|
extern void AlterEventTriggerOwner_oid(Oid, Oid newOwnerId); |
||||||
|
|
||||||
|
#endif /* EVENT_TRIGGER_H */ |
@ -0,0 +1,90 @@ |
|||||||
|
-- should fail, return type mismatch |
||||||
|
create event trigger regress_event_trigger |
||||||
|
on ddl_command_start |
||||||
|
execute procedure pg_backend_pid(); |
||||||
|
ERROR: function "pg_backend_pid" must return type "event_trigger" |
||||||
|
-- cheesy hack for testing purposes |
||||||
|
create function fake_event_trigger() |
||||||
|
returns event_trigger |
||||||
|
language internal |
||||||
|
as 'pg_backend_pid'; |
||||||
|
-- should fail, no elephant_bootstrap entry point |
||||||
|
create event trigger regress_event_trigger on elephant_bootstrap |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
ERROR: unrecognized event name "elephant_bootstrap" |
||||||
|
-- OK |
||||||
|
create event trigger regress_event_trigger on ddl_command_start |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
-- should fail, food is not a valid filter variable |
||||||
|
create event trigger regress_event_trigger2 on ddl_command_start |
||||||
|
when food in ('sandwhich') |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
ERROR: unrecognized filter variable "food" |
||||||
|
-- should fail, sandwhich is not a valid command tag |
||||||
|
create event trigger regress_event_trigger2 on ddl_command_start |
||||||
|
when tag in ('sandwhich') |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
ERROR: filter value "sandwhich" not recognized for filter variable "tag" |
||||||
|
-- should fail, create skunkcabbage is not a valid comand tag |
||||||
|
create event trigger regress_event_trigger2 on ddl_command_start |
||||||
|
when tag in ('create table', 'create skunkcabbage') |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
ERROR: filter value "create skunkcabbage" not recognized for filter variable "tag" |
||||||
|
-- should fail, can't have event triggers on event triggers |
||||||
|
create event trigger regress_event_trigger2 on ddl_command_start |
||||||
|
when tag in ('DROP EVENT TRIGGER') |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
ERROR: event triggers are not supported for "DROP EVENT TRIGGER" |
||||||
|
-- should fail, can't have same filter variable twice |
||||||
|
create event trigger regress_event_trigger2 on ddl_command_start |
||||||
|
when tag in ('create table') and tag in ('CREATE FUNCTION') |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
ERROR: filter variable "tag" specified more than once |
||||||
|
-- OK |
||||||
|
create event trigger regress_event_trigger2 on ddl_command_start |
||||||
|
when tag in ('create table', 'CREATE FUNCTION') |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
-- OK |
||||||
|
comment on event trigger regress_event_trigger is 'test comment'; |
||||||
|
-- should fail, event triggers are not schema objects |
||||||
|
comment on event trigger wrong.regress_event_trigger is 'test comment'; |
||||||
|
ERROR: event trigger name cannot be qualified |
||||||
|
-- drop as non-superuser should fail |
||||||
|
create role regression_bob; |
||||||
|
set role regression_bob; |
||||||
|
create event trigger regress_event_trigger_noperms on ddl_command_start |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
ERROR: permission denied to create event trigger "regress_event_trigger_noperms" |
||||||
|
HINT: Must be superuser to create an event trigger. |
||||||
|
reset role; |
||||||
|
-- all OK |
||||||
|
alter event trigger regress_event_trigger disable; |
||||||
|
alter event trigger regress_event_trigger enable replica; |
||||||
|
alter event trigger regress_event_trigger enable always; |
||||||
|
alter event trigger regress_event_trigger enable; |
||||||
|
-- alter owner to non-superuser should fail |
||||||
|
alter event trigger regress_event_trigger owner to regression_bob; |
||||||
|
ERROR: permission denied to change owner of event trigger "regress_event_trigger" |
||||||
|
HINT: The owner of an event trigger must be a superuser. |
||||||
|
-- alter owner to superuser should work |
||||||
|
alter role regression_bob superuser; |
||||||
|
alter event trigger regress_event_trigger owner to regression_bob; |
||||||
|
-- should fail, name collision |
||||||
|
alter event trigger regress_event_trigger rename to regress_event_trigger2; |
||||||
|
ERROR: event trigger "regress_event_trigger2" already exists |
||||||
|
-- OK |
||||||
|
alter event trigger regress_event_trigger rename to regress_event_trigger3; |
||||||
|
-- should fail, doesn't exist any more |
||||||
|
drop event trigger regress_event_trigger; |
||||||
|
ERROR: event trigger "regress_event_trigger" does not exist |
||||||
|
-- should fail, regression_bob owns regress_event_trigger2/3 |
||||||
|
drop role regression_bob; |
||||||
|
ERROR: role "regression_bob" cannot be dropped because some objects depend on it |
||||||
|
DETAIL: owner of event trigger regress_event_trigger3 |
||||||
|
-- these are all OK; the second one should emit a NOTICE |
||||||
|
drop event trigger if exists regress_event_trigger2; |
||||||
|
drop event trigger if exists regress_event_trigger2; |
||||||
|
NOTICE: event trigger "regress_event_trigger2" does not exist, skipping |
||||||
|
drop event trigger regress_event_trigger3; |
||||||
|
drop function fake_event_trigger(); |
||||||
|
drop role regression_bob; |
@ -0,0 +1,93 @@ |
|||||||
|
-- should fail, return type mismatch |
||||||
|
create event trigger regress_event_trigger |
||||||
|
on ddl_command_start |
||||||
|
execute procedure pg_backend_pid(); |
||||||
|
|
||||||
|
-- cheesy hack for testing purposes |
||||||
|
create function fake_event_trigger() |
||||||
|
returns event_trigger |
||||||
|
language internal |
||||||
|
as 'pg_backend_pid'; |
||||||
|
|
||||||
|
-- should fail, no elephant_bootstrap entry point |
||||||
|
create event trigger regress_event_trigger on elephant_bootstrap |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
|
||||||
|
-- OK |
||||||
|
create event trigger regress_event_trigger on ddl_command_start |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
|
||||||
|
-- should fail, food is not a valid filter variable |
||||||
|
create event trigger regress_event_trigger2 on ddl_command_start |
||||||
|
when food in ('sandwhich') |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
|
||||||
|
-- should fail, sandwhich is not a valid command tag |
||||||
|
create event trigger regress_event_trigger2 on ddl_command_start |
||||||
|
when tag in ('sandwhich') |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
|
||||||
|
-- should fail, create skunkcabbage is not a valid comand tag |
||||||
|
create event trigger regress_event_trigger2 on ddl_command_start |
||||||
|
when tag in ('create table', 'create skunkcabbage') |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
|
||||||
|
-- should fail, can't have event triggers on event triggers |
||||||
|
create event trigger regress_event_trigger2 on ddl_command_start |
||||||
|
when tag in ('DROP EVENT TRIGGER') |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
|
||||||
|
-- should fail, can't have same filter variable twice |
||||||
|
create event trigger regress_event_trigger2 on ddl_command_start |
||||||
|
when tag in ('create table') and tag in ('CREATE FUNCTION') |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
|
||||||
|
-- OK |
||||||
|
create event trigger regress_event_trigger2 on ddl_command_start |
||||||
|
when tag in ('create table', 'CREATE FUNCTION') |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
|
||||||
|
-- OK |
||||||
|
comment on event trigger regress_event_trigger is 'test comment'; |
||||||
|
|
||||||
|
-- should fail, event triggers are not schema objects |
||||||
|
comment on event trigger wrong.regress_event_trigger is 'test comment'; |
||||||
|
|
||||||
|
-- drop as non-superuser should fail |
||||||
|
create role regression_bob; |
||||||
|
set role regression_bob; |
||||||
|
create event trigger regress_event_trigger_noperms on ddl_command_start |
||||||
|
execute procedure fake_event_trigger(); |
||||||
|
reset role; |
||||||
|
|
||||||
|
-- all OK |
||||||
|
alter event trigger regress_event_trigger disable; |
||||||
|
alter event trigger regress_event_trigger enable replica; |
||||||
|
alter event trigger regress_event_trigger enable always; |
||||||
|
alter event trigger regress_event_trigger enable; |
||||||
|
|
||||||
|
-- alter owner to non-superuser should fail |
||||||
|
alter event trigger regress_event_trigger owner to regression_bob; |
||||||
|
|
||||||
|
-- alter owner to superuser should work |
||||||
|
alter role regression_bob superuser; |
||||||
|
alter event trigger regress_event_trigger owner to regression_bob; |
||||||
|
|
||||||
|
-- should fail, name collision |
||||||
|
alter event trigger regress_event_trigger rename to regress_event_trigger2; |
||||||
|
|
||||||
|
-- OK |
||||||
|
alter event trigger regress_event_trigger rename to regress_event_trigger3; |
||||||
|
|
||||||
|
-- should fail, doesn't exist any more |
||||||
|
drop event trigger regress_event_trigger; |
||||||
|
|
||||||
|
-- should fail, regression_bob owns regress_event_trigger2/3 |
||||||
|
drop role regression_bob; |
||||||
|
|
||||||
|
-- these are all OK; the second one should emit a NOTICE |
||||||
|
drop event trigger if exists regress_event_trigger2; |
||||||
|
drop event trigger if exists regress_event_trigger2; |
||||||
|
drop event trigger regress_event_trigger3; |
||||||
|
drop function fake_event_trigger(); |
||||||
|
drop role regression_bob; |
Loading…
Reference in new issue