mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-27 08:39:28 +08:00
Syntax support and documentation for event triggers.
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.
This commit is contained in:
parent
faf26bf117
commit
3855968f32
@ -143,6 +143,11 @@
|
||||
<entry>enum label and value definitions</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><link linkend="catalog-pg-event-trigger"><structname>pg_event_trigger</structname></link></entry>
|
||||
<entry>event triggers</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><link linkend="catalog-pg-extension"><structname>pg_extension</structname></link></entry>
|
||||
<entry>installed extensions</entry>
|
||||
@ -1857,6 +1862,88 @@
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="catalog-pg-event-trigger">
|
||||
<title><structname>pg_event_trigger</structname></title>
|
||||
|
||||
<indexterm zone="catalog-pg-event-trigger">
|
||||
<primary>pg_event_trigger</primary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
The catalog <structname>pg_event_trigger</structname> stores event triggers.
|
||||
See <xref linkend="event-triggers"> for more information.
|
||||
</para>
|
||||
|
||||
<table>
|
||||
<title><structname>pg_event_trigger</> Columns</title>
|
||||
|
||||
<tgroup cols="4">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Name</entry>
|
||||
<entry>Type</entry>
|
||||
<entry>References</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><structfield>evtname</structfield></entry>
|
||||
<entry><type>name</type></entry>
|
||||
<entry></entry>
|
||||
<entry>Trigger name (must be unique)</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>evtevent</structfield></entry>
|
||||
<entry><type>name</type></entry>
|
||||
<entry></entry>
|
||||
<entry>Identifies the event for which this trigger fires</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>evtowner</structfield></entry>
|
||||
<entry><type>oid</type></entry>
|
||||
<entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
|
||||
<entry>Owner of the event trigger</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>evtfoid</structfield></entry>
|
||||
<entry><type>oid</type></entry>
|
||||
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
|
||||
<entry>The function to be called</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>evtenabled</structfield></entry>
|
||||
<entry><type>char</type></entry>
|
||||
<entry></entry>
|
||||
<entry>
|
||||
Controls in which <xref linkend="guc-session-replication-role"> modes
|
||||
the event trigger fires.
|
||||
<literal>O</> = trigger fires in <quote>origin</> and <quote>local</> modes,
|
||||
<literal>D</> = trigger is disabled,
|
||||
<literal>R</> = trigger fires in <quote>replica</> mode,
|
||||
<literal>A</> = trigger fires always.
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>evttags</structfield></entry>
|
||||
<entry><type>text[]</type></entry>
|
||||
<entry></entry>
|
||||
<entry>
|
||||
Command tags for which this trigger will fire. If NULL, the firing
|
||||
of this trigger is not restricted on the basis of the command tag.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="catalog-pg-constraint">
|
||||
<title><structname>pg_constraint</structname></title>
|
||||
|
||||
|
415
doc/src/sgml/event-trigger.sgml
Normal file
415
doc/src/sgml/event-trigger.sgml
Normal file
@ -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>
|
@ -61,6 +61,7 @@
|
||||
<!ENTITY rules SYSTEM "rules.sgml">
|
||||
<!ENTITY spi SYSTEM "spi.sgml">
|
||||
<!ENTITY trigger SYSTEM "trigger.sgml">
|
||||
<!ENTITY event-trigger SYSTEM "event-trigger.sgml">
|
||||
<!ENTITY xaggr SYSTEM "xaggr.sgml">
|
||||
<!ENTITY xfunc SYSTEM "xfunc.sgml">
|
||||
<!ENTITY xindex SYSTEM "xindex.sgml">
|
||||
|
@ -208,6 +208,7 @@
|
||||
|
||||
&extend;
|
||||
&trigger;
|
||||
&event-trigger;
|
||||
&rules;
|
||||
|
||||
&xplang;
|
||||
|
@ -12,6 +12,7 @@ Complete list of usable sgml source files in this directory.
|
||||
<!ENTITY alterDatabase SYSTEM "alter_database.sgml">
|
||||
<!ENTITY alterDefaultPrivileges SYSTEM "alter_default_privileges.sgml">
|
||||
<!ENTITY alterDomain SYSTEM "alter_domain.sgml">
|
||||
<!ENTITY alterEventTrigger SYSTEM "alter_event_trigger.sgml">
|
||||
<!ENTITY alterExtension SYSTEM "alter_extension.sgml">
|
||||
<!ENTITY alterForeignDataWrapper SYSTEM "alter_foreign_data_wrapper.sgml">
|
||||
<!ENTITY alterForeignTable SYSTEM "alter_foreign_table.sgml">
|
||||
@ -53,6 +54,7 @@ Complete list of usable sgml source files in this directory.
|
||||
<!ENTITY createConversion SYSTEM "create_conversion.sgml">
|
||||
<!ENTITY createDatabase SYSTEM "create_database.sgml">
|
||||
<!ENTITY createDomain SYSTEM "create_domain.sgml">
|
||||
<!ENTITY createEventTrigger SYSTEM "create_event_trigger.sgml">
|
||||
<!ENTITY createExtension SYSTEM "create_extension.sgml">
|
||||
<!ENTITY createForeignDataWrapper SYSTEM "create_foreign_data_wrapper.sgml">
|
||||
<!ENTITY createForeignTable SYSTEM "create_foreign_table.sgml">
|
||||
@ -91,6 +93,7 @@ Complete list of usable sgml source files in this directory.
|
||||
<!ENTITY dropConversion SYSTEM "drop_conversion.sgml">
|
||||
<!ENTITY dropDatabase SYSTEM "drop_database.sgml">
|
||||
<!ENTITY dropDomain SYSTEM "drop_domain.sgml">
|
||||
<!ENTITY dropEventTrigger SYSTEM "drop_event_trigger.sgml">
|
||||
<!ENTITY dropExtension SYSTEM "drop_extension.sgml">
|
||||
<!ENTITY dropForeignDataWrapper SYSTEM "drop_foreign_data_wrapper.sgml">
|
||||
<!ENTITY dropForeignTable SYSTEM "drop_foreign_table.sgml">
|
||||
|
105
doc/src/sgml/ref/alter_event_trigger.sgml
Normal file
105
doc/src/sgml/ref/alter_event_trigger.sgml
Normal file
@ -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>
|
@ -35,6 +35,7 @@ ALTER EXTENSION <replaceable class="PARAMETER">name</replaceable> DROP <replacea
|
||||
COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
CONVERSION <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
EVENT TRIGGER <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
FOREIGN DATA WRAPPER <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
FOREIGN TABLE <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
FUNCTION <replaceable class="PARAMETER">function_name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) |
|
||||
|
@ -32,6 +32,7 @@ COMMENT ON
|
||||
DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
EXTENSION <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
EVENT TRIGGER <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
FOREIGN DATA WRAPPER <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
FOREIGN TABLE <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
FUNCTION <replaceable class="PARAMETER">function_name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) |
|
||||
|
162
doc/src/sgml/ref/create_event_trigger.sgml
Normal file
162
doc/src/sgml/ref/create_event_trigger.sgml
Normal file
@ -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>
|
113
doc/src/sgml/ref/drop_event_trigger.sgml
Normal file
113
doc/src/sgml/ref/drop_event_trigger.sgml
Normal file
@ -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>
|
@ -1455,6 +1455,20 @@ testdb=>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><literal>\dy[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Lists event triggers.
|
||||
If <replaceable class="parameter">pattern</replaceable>
|
||||
is specified, only those event triggers whose names match the pattern
|
||||
are listed.
|
||||
If <literal>+</literal> is appended to the command name, each object
|
||||
is listed with its associated description.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><literal>\e</literal> or <literal>\edit</> <literal> <optional> <replaceable class="parameter">filename</> </optional> <optional> <replaceable class="parameter">line_number</> </optional> </literal></term>
|
||||
|
||||
|
@ -28,6 +28,7 @@ SECURITY LABEL [ FOR <replaceable class="PARAMETER">provider</replaceable> ] ON
|
||||
AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable> [, ...] ) |
|
||||
DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
EVENT TRIGGER <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
FOREIGN TABLE <replaceable class="PARAMETER">object_name</replaceable>
|
||||
FUNCTION <replaceable class="PARAMETER">function_name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) |
|
||||
LARGE OBJECT <replaceable class="PARAMETER">large_object_oid</replaceable> |
|
||||
|
@ -41,6 +41,7 @@
|
||||
&alterDefaultPrivileges;
|
||||
&alterDomain;
|
||||
&alterExtension;
|
||||
&alterEventTrigger;
|
||||
&alterForeignDataWrapper;
|
||||
&alterForeignTable;
|
||||
&alterFunction;
|
||||
@ -82,6 +83,7 @@
|
||||
&createDatabase;
|
||||
&createDomain;
|
||||
&createExtension;
|
||||
&createEventTrigger;
|
||||
&createForeignDataWrapper;
|
||||
&createForeignTable;
|
||||
&createFunction;
|
||||
@ -120,6 +122,7 @@
|
||||
&dropDatabase;
|
||||
&dropDomain;
|
||||
&dropExtension;
|
||||
&dropEventTrigger;
|
||||
&dropForeignDataWrapper;
|
||||
&dropForeignTable;
|
||||
&dropFunction;
|
||||
|
@ -31,7 +31,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
|
||||
pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
|
||||
pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
|
||||
pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
|
||||
pg_statistic.h pg_rewrite.h pg_trigger.h pg_description.h \
|
||||
pg_statistic.h pg_rewrite.h pg_trigger.h pg_event_trigger.h pg_description.h \
|
||||
pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
|
||||
pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \
|
||||
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "catalog/pg_conversion.h"
|
||||
#include "catalog/pg_database.h"
|
||||
#include "catalog/pg_default_acl.h"
|
||||
#include "catalog/pg_event_trigger.h"
|
||||
#include "catalog/pg_extension.h"
|
||||
#include "catalog/pg_foreign_data_wrapper.h"
|
||||
#include "catalog/pg_foreign_server.h"
|
||||
@ -277,6 +278,10 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
|
||||
case ACL_KIND_FOREIGN_SERVER:
|
||||
whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
|
||||
break;
|
||||
case ACL_KIND_EVENT_TRIGGER:
|
||||
elog(ERROR, "grantable rights not supported for event triggers");
|
||||
/* not reached, but keep compiler quiet */
|
||||
return ACL_NO_RIGHTS;
|
||||
case ACL_KIND_TYPE:
|
||||
whole_mask = ACL_ALL_RIGHTS_TYPE;
|
||||
break;
|
||||
@ -3286,6 +3291,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
|
||||
gettext_noop("permission denied for foreign-data wrapper %s"),
|
||||
/* ACL_KIND_FOREIGN_SERVER */
|
||||
gettext_noop("permission denied for foreign server %s"),
|
||||
/* ACL_KIND_EVENT_TRIGGER */
|
||||
gettext_noop("permission denied for event trigger %s"),
|
||||
/* ACL_KIND_EXTENSION */
|
||||
gettext_noop("permission denied for extension %s"),
|
||||
};
|
||||
@ -3330,6 +3337,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] =
|
||||
gettext_noop("must be owner of foreign-data wrapper %s"),
|
||||
/* ACL_KIND_FOREIGN_SERVER */
|
||||
gettext_noop("must be owner of foreign server %s"),
|
||||
/* ACL_KIND_EVENT_TRIGGER */
|
||||
gettext_noop("must be owner of event trigger %s"),
|
||||
/* ACL_KIND_EXTENSION */
|
||||
gettext_noop("must be owner of extension %s"),
|
||||
};
|
||||
@ -3455,6 +3464,10 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid,
|
||||
return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how);
|
||||
case ACL_KIND_FOREIGN_SERVER:
|
||||
return pg_foreign_server_aclmask(table_oid, roleid, mask, how);
|
||||
case ACL_KIND_EVENT_TRIGGER:
|
||||
elog(ERROR, "grantable rights not supported for event triggers");
|
||||
/* not reached, but keep compiler quiet */
|
||||
return ACL_NO_RIGHTS;
|
||||
case ACL_KIND_TYPE:
|
||||
return pg_type_aclmask(table_oid, roleid, mask, how);
|
||||
default:
|
||||
@ -4875,6 +4888,33 @@ pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid)
|
||||
return has_privs_of_role(roleid, ownerId);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ownership check for an event trigger (specified by OID).
|
||||
*/
|
||||
bool
|
||||
pg_event_trigger_ownercheck(Oid et_oid, Oid roleid)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
Oid ownerId;
|
||||
|
||||
/* Superusers bypass all permission checking. */
|
||||
if (superuser_arg(roleid))
|
||||
return true;
|
||||
|
||||
tuple = SearchSysCache1(EVENTTRIGGEROID, ObjectIdGetDatum(et_oid));
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("event trigger with OID %u does not exist",
|
||||
et_oid)));
|
||||
|
||||
ownerId = ((Form_pg_event_trigger) GETSTRUCT(tuple))->evtowner;
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
return has_privs_of_role(roleid, ownerId);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ownership check for a database (specified by OID).
|
||||
*/
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "catalog/pg_database.h"
|
||||
#include "catalog/pg_default_acl.h"
|
||||
#include "catalog/pg_depend.h"
|
||||
#include "catalog/pg_event_trigger.h"
|
||||
#include "catalog/pg_extension.h"
|
||||
#include "catalog/pg_foreign_data_wrapper.h"
|
||||
#include "catalog/pg_foreign_server.h"
|
||||
@ -56,6 +57,7 @@
|
||||
#include "commands/comment.h"
|
||||
#include "commands/dbcommands.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "commands/event_trigger.h"
|
||||
#include "commands/extension.h"
|
||||
#include "commands/proclang.h"
|
||||
#include "commands/schemacmds.h"
|
||||
@ -158,7 +160,8 @@ static const Oid object_classes[MAX_OCLASS] = {
|
||||
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
|
||||
UserMappingRelationId, /* OCLASS_USER_MAPPING */
|
||||
DefaultAclRelationId, /* OCLASS_DEFACL */
|
||||
ExtensionRelationId /* OCLASS_EXTENSION */
|
||||
ExtensionRelationId, /* OCLASS_EXTENSION */
|
||||
EventTriggerRelationId /* OCLASS_EVENT_TRIGGER */
|
||||
};
|
||||
|
||||
|
||||
@ -1112,6 +1115,10 @@ doDeletion(const ObjectAddress *object, int flags)
|
||||
break;
|
||||
}
|
||||
|
||||
case OCLASS_EVENT_TRIGGER:
|
||||
RemoveEventTriggerById(object->objectId);
|
||||
break;
|
||||
|
||||
case OCLASS_PROC:
|
||||
RemoveFunctionById(object->objectId);
|
||||
break;
|
||||
@ -2269,6 +2276,9 @@ getObjectClass(const ObjectAddress *object)
|
||||
|
||||
case ExtensionRelationId:
|
||||
return OCLASS_EXTENSION;
|
||||
|
||||
case EventTriggerRelationId:
|
||||
return OCLASS_EVENT_TRIGGER;
|
||||
}
|
||||
|
||||
/* shouldn't get here */
|
||||
@ -2903,6 +2913,21 @@ getObjectDescription(const ObjectAddress *object)
|
||||
break;
|
||||
}
|
||||
|
||||
case OCLASS_EVENT_TRIGGER:
|
||||
{
|
||||
HeapTuple tup;
|
||||
|
||||
tup = SearchSysCache1(EVENTTRIGGEROID,
|
||||
ObjectIdGetDatum(object->objectId));
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "cache lookup failed for event trigger %u",
|
||||
object->objectId);
|
||||
appendStringInfo(&buffer, _("event trigger %s"),
|
||||
NameStr(((Form_pg_event_trigger) GETSTRUCT(tup))->evtname));
|
||||
ReleaseSysCache(tup);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
appendStringInfo(&buffer, "unrecognized object %u %u %d",
|
||||
object->classId,
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "catalog/objectaddress.h"
|
||||
#include "catalog/pg_authid.h"
|
||||
#include "catalog/pg_cast.h"
|
||||
#include "catalog/pg_event_trigger.h"
|
||||
#include "catalog/pg_collation.h"
|
||||
#include "catalog/pg_constraint.h"
|
||||
#include "catalog/pg_conversion.h"
|
||||
@ -46,6 +47,7 @@
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/dbcommands.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "commands/event_trigger.h"
|
||||
#include "commands/extension.h"
|
||||
#include "commands/proclang.h"
|
||||
#include "commands/tablespace.h"
|
||||
@ -203,6 +205,12 @@ static ObjectPropertyType ObjectProperty[] =
|
||||
-1,
|
||||
InvalidAttrNumber
|
||||
},
|
||||
{
|
||||
EventTriggerRelationId,
|
||||
EventTriggerOidIndexId,
|
||||
-1,
|
||||
InvalidAttrNumber
|
||||
},
|
||||
{
|
||||
TSConfigRelationId,
|
||||
TSConfigOidIndexId,
|
||||
@ -325,6 +333,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
|
||||
case OBJECT_LANGUAGE:
|
||||
case OBJECT_FDW:
|
||||
case OBJECT_FOREIGN_SERVER:
|
||||
case OBJECT_EVENT_TRIGGER:
|
||||
address = get_object_address_unqualified(objtype,
|
||||
objname, missing_ok);
|
||||
break;
|
||||
@ -546,6 +555,9 @@ get_object_address_unqualified(ObjectType objtype,
|
||||
case OBJECT_FOREIGN_SERVER:
|
||||
msg = gettext_noop("server name cannot be qualified");
|
||||
break;
|
||||
case OBJECT_EVENT_TRIGGER:
|
||||
msg = gettext_noop("event trigger name cannot be qualified");
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
|
||||
msg = NULL; /* placate compiler */
|
||||
@ -601,6 +613,11 @@ get_object_address_unqualified(ObjectType objtype,
|
||||
address.objectId = get_foreign_server_oid(name, missing_ok);
|
||||
address.objectSubId = 0;
|
||||
break;
|
||||
case OBJECT_EVENT_TRIGGER:
|
||||
address.classId = EventTriggerRelationId;
|
||||
address.objectId = get_event_trigger_oid(name, missing_ok);
|
||||
address.objectSubId = 0;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
|
||||
/* placate compiler, which doesn't know elog won't return */
|
||||
@ -980,6 +997,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
|
||||
NameListToString(objname));
|
||||
break;
|
||||
case OBJECT_EVENT_TRIGGER:
|
||||
if (!pg_event_trigger_ownercheck(address.objectId, roleid))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
|
||||
NameListToString(objname));
|
||||
break;
|
||||
case OBJECT_LANGUAGE:
|
||||
if (!pg_language_ownercheck(address.objectId, roleid))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE,
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "catalog/pg_conversion.h"
|
||||
#include "catalog/pg_database.h"
|
||||
#include "catalog/pg_default_acl.h"
|
||||
#include "catalog/pg_event_trigger.h"
|
||||
#include "catalog/pg_extension.h"
|
||||
#include "catalog/pg_foreign_data_wrapper.h"
|
||||
#include "catalog/pg_foreign_server.h"
|
||||
@ -42,6 +43,7 @@
|
||||
#include "commands/collationcmds.h"
|
||||
#include "commands/conversioncmds.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "commands/event_trigger.h"
|
||||
#include "commands/extension.h"
|
||||
#include "commands/proclang.h"
|
||||
#include "commands/schemacmds.h"
|
||||
@ -1398,6 +1400,10 @@ shdepReassignOwned(List *roleids, Oid newrole)
|
||||
AlterExtensionOwner_oid(sdepForm->objid, newrole);
|
||||
break;
|
||||
|
||||
case EventTriggerRelationId:
|
||||
AlterEventTriggerOwner_oid(sdepForm->objid, newrole);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "unexpected classid %u", sdepForm->classid);
|
||||
break;
|
||||
|
@ -310,6 +310,19 @@ FROM
|
||||
WHERE
|
||||
l.objsubid = 0
|
||||
UNION ALL
|
||||
SELECT
|
||||
l.objoid, l.classoid, l.objsubid,
|
||||
'event trigger'::text AS objtype,
|
||||
NULL::oid AS objnamespace,
|
||||
quote_ident(evt.evtname) AS objname,
|
||||
l.provider, l.label
|
||||
FROM
|
||||
pg_seclabel l
|
||||
JOIN pg_event_trigger evt ON l.classoid = evt.tableoid
|
||||
AND l.objoid = evt.oid
|
||||
WHERE
|
||||
l.objsubid = 0
|
||||
UNION ALL
|
||||
SELECT
|
||||
l.objoid, l.classoid, 0::int4 AS objsubid,
|
||||
'database'::text AS objtype,
|
||||
|
@ -14,8 +14,8 @@ include $(top_builddir)/src/Makefile.global
|
||||
|
||||
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
|
||||
collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
|
||||
dbcommands.o define.o discard.o dropcmds.o explain.o extension.o \
|
||||
foreigncmds.o functioncmds.o \
|
||||
dbcommands.o define.o discard.o dropcmds.o \
|
||||
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
|
||||
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
|
||||
portalcmds.o prepare.o proclang.o \
|
||||
schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "commands/conversioncmds.h"
|
||||
#include "commands/dbcommands.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "commands/event_trigger.h"
|
||||
#include "commands/extension.h"
|
||||
#include "commands/proclang.h"
|
||||
#include "commands/schemacmds.h"
|
||||
@ -77,6 +78,10 @@ ExecRenameStmt(RenameStmt *stmt)
|
||||
RenameForeignServer(stmt->subname, stmt->newname);
|
||||
break;
|
||||
|
||||
case OBJECT_EVENT_TRIGGER:
|
||||
RenameEventTrigger(stmt->subname, stmt->newname);
|
||||
break;
|
||||
|
||||
case OBJECT_FUNCTION:
|
||||
RenameFunction(stmt->object, stmt->objarg, stmt->newname);
|
||||
break;
|
||||
@ -534,6 +539,10 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
|
||||
AlterForeignServerOwner(strVal(linitial(stmt->object)), newowner);
|
||||
break;
|
||||
|
||||
case OBJECT_EVENT_TRIGGER:
|
||||
AlterEventTriggerOwner(strVal(linitial(stmt->object)), newowner);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "unrecognized AlterOwnerStmt type: %d",
|
||||
(int) stmt->objectType);
|
||||
|
@ -206,6 +206,10 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs)
|
||||
args = NameListToString(list_truncate(objname,
|
||||
list_length(objname) - 1));
|
||||
break;
|
||||
case OBJECT_EVENT_TRIGGER:
|
||||
msg = gettext_noop("event trigger \"%s\" does not exist, skipping");
|
||||
name = NameListToString(objname);
|
||||
break;
|
||||
case OBJECT_RULE:
|
||||
msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping");
|
||||
name = strVal(llast(objname));
|
||||
|
539
src/backend/commands/event_trigger.c
Normal file
539
src/backend/commands/event_trigger.c
Normal file
@ -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;
|
||||
}
|
@ -3466,6 +3466,30 @@ _copyCreateTrigStmt(const CreateTrigStmt *from)
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static CreateEventTrigStmt *
|
||||
_copyCreateEventTrigStmt(const CreateEventTrigStmt *from)
|
||||
{
|
||||
CreateEventTrigStmt *newnode = makeNode(CreateEventTrigStmt);
|
||||
|
||||
COPY_STRING_FIELD(trigname);
|
||||
COPY_SCALAR_FIELD(eventname);
|
||||
COPY_NODE_FIELD(whenclause);
|
||||
COPY_NODE_FIELD(funcname);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static AlterEventTrigStmt *
|
||||
_copyAlterEventTrigStmt(const AlterEventTrigStmt *from)
|
||||
{
|
||||
AlterEventTrigStmt *newnode = makeNode(AlterEventTrigStmt);
|
||||
|
||||
COPY_STRING_FIELD(trigname);
|
||||
COPY_SCALAR_FIELD(tgenabled);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static CreatePLangStmt *
|
||||
_copyCreatePLangStmt(const CreatePLangStmt *from)
|
||||
{
|
||||
@ -4317,6 +4341,12 @@ copyObject(const void *from)
|
||||
case T_CreateTrigStmt:
|
||||
retval = _copyCreateTrigStmt(from);
|
||||
break;
|
||||
case T_CreateEventTrigStmt:
|
||||
retval = _copyCreateEventTrigStmt(from);
|
||||
break;
|
||||
case T_AlterEventTrigStmt:
|
||||
retval = _copyAlterEventTrigStmt(from);
|
||||
break;
|
||||
case T_CreatePLangStmt:
|
||||
retval = _copyCreatePLangStmt(from);
|
||||
break;
|
||||
|
@ -1792,6 +1792,26 @@ _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalCreateEventTrigStmt(const CreateEventTrigStmt *a, const CreateEventTrigStmt *b)
|
||||
{
|
||||
COMPARE_STRING_FIELD(trigname);
|
||||
COMPARE_SCALAR_FIELD(eventname);
|
||||
COMPARE_NODE_FIELD(funcname);
|
||||
COMPARE_NODE_FIELD(whenclause);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalAlterEventTrigStmt(const AlterEventTrigStmt *a, const AlterEventTrigStmt *b)
|
||||
{
|
||||
COMPARE_STRING_FIELD(trigname);
|
||||
COMPARE_SCALAR_FIELD(tgenabled);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalCreatePLangStmt(const CreatePLangStmt *a, const CreatePLangStmt *b)
|
||||
{
|
||||
@ -2872,6 +2892,12 @@ equal(const void *a, const void *b)
|
||||
case T_CreateTrigStmt:
|
||||
retval = _equalCreateTrigStmt(a, b);
|
||||
break;
|
||||
case T_CreateEventTrigStmt:
|
||||
retval = _equalCreateEventTrigStmt(a, b);
|
||||
break;
|
||||
case T_AlterEventTrigStmt:
|
||||
retval = _equalAlterEventTrigStmt(a, b);
|
||||
break;
|
||||
case T_CreatePLangStmt:
|
||||
retval = _equalCreatePLangStmt(a, b);
|
||||
break;
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_trigger.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#include "parser/gramparse.h"
|
||||
@ -194,6 +195,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
|
||||
}
|
||||
|
||||
%type <node> stmt schema_stmt
|
||||
AlterEventTrigStmt
|
||||
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
|
||||
AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
|
||||
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
|
||||
@ -207,7 +209,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
|
||||
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
|
||||
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
|
||||
CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
|
||||
CreateAssertStmt CreateTrigStmt
|
||||
CreateAssertStmt CreateTrigStmt CreateEventTrigStmt
|
||||
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
|
||||
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
|
||||
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
|
||||
@ -268,6 +270,10 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
|
||||
%type <value> TriggerFuncArg
|
||||
%type <node> TriggerWhen
|
||||
|
||||
%type <list> event_trigger_when_list event_trigger_value_list
|
||||
%type <defelt> event_trigger_when_item
|
||||
%type <chr> enable_trigger
|
||||
|
||||
%type <str> copy_file_name
|
||||
database_name access_method_clause access_method attr_name
|
||||
name cursor_name file_name
|
||||
@ -505,7 +511,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
|
||||
DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS 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 EXCEPT
|
||||
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
|
||||
EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
|
||||
EXTENSION EXTERNAL EXTRACT
|
||||
|
||||
@ -674,7 +680,8 @@ stmtmulti: stmtmulti ';' stmt
|
||||
;
|
||||
|
||||
stmt :
|
||||
AlterDatabaseStmt
|
||||
AlterEventTrigStmt
|
||||
| AlterDatabaseStmt
|
||||
| AlterDatabaseSetStmt
|
||||
| AlterDefaultPrivilegesStmt
|
||||
| AlterDomainStmt
|
||||
@ -725,6 +732,7 @@ stmt :
|
||||
| CreateStmt
|
||||
| CreateTableSpaceStmt
|
||||
| CreateTrigStmt
|
||||
| CreateEventTrigStmt
|
||||
| CreateRoleStmt
|
||||
| CreateUserStmt
|
||||
| CreateUserMappingStmt
|
||||
@ -3554,6 +3562,15 @@ AlterExtensionContentsStmt:
|
||||
n->objname = list_make1(makeString($6));
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| ALTER EXTENSION name add_drop EVENT TRIGGER name
|
||||
{
|
||||
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
|
||||
n->extname = $3;
|
||||
n->action = $4;
|
||||
n->objtype = OBJECT_EVENT_TRIGGER;
|
||||
n->objname = list_make1(makeString($7));
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| ALTER EXTENSION name add_drop TABLE any_name
|
||||
{
|
||||
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
|
||||
@ -4282,6 +4299,75 @@ DropTrigStmt:
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUERIES :
|
||||
* CREATE EVENT TRIGGER ...
|
||||
* DROP EVENT TRIGGER ...
|
||||
* ALTER EVENT TRIGGER ...
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
CreateEventTrigStmt:
|
||||
CREATE EVENT TRIGGER name ON ColLabel
|
||||
EXECUTE PROCEDURE func_name '(' ')'
|
||||
{
|
||||
CreateEventTrigStmt *n = makeNode(CreateEventTrigStmt);
|
||||
n->trigname = $4;
|
||||
n->eventname = $6;
|
||||
n->whenclause = NULL;
|
||||
n->funcname = $9;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| CREATE EVENT TRIGGER name ON ColLabel
|
||||
WHEN event_trigger_when_list
|
||||
EXECUTE PROCEDURE func_name '(' ')'
|
||||
{
|
||||
CreateEventTrigStmt *n = makeNode(CreateEventTrigStmt);
|
||||
n->trigname = $4;
|
||||
n->eventname = $6;
|
||||
n->whenclause = $8;
|
||||
n->funcname = $11;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
|
||||
event_trigger_when_list:
|
||||
event_trigger_when_item
|
||||
{ $$ = list_make1($1); }
|
||||
| event_trigger_when_list AND event_trigger_when_item
|
||||
{ $$ = lappend($1, $3); }
|
||||
;
|
||||
|
||||
event_trigger_when_item:
|
||||
ColId IN_P '(' event_trigger_value_list ')'
|
||||
{ $$ = makeDefElem($1, (Node *) $4); }
|
||||
;
|
||||
|
||||
event_trigger_value_list:
|
||||
SCONST
|
||||
{ $$ = list_make1(makeString($1)); }
|
||||
| event_trigger_value_list ',' SCONST
|
||||
{ $$ = lappend($1, makeString($3)); }
|
||||
;
|
||||
|
||||
AlterEventTrigStmt:
|
||||
ALTER EVENT TRIGGER name enable_trigger
|
||||
{
|
||||
AlterEventTrigStmt *n = makeNode(AlterEventTrigStmt);
|
||||
n->trigname = $4;
|
||||
n->tgenabled = $5;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
enable_trigger:
|
||||
ENABLE_P { $$ = TRIGGER_FIRES_ON_ORIGIN; }
|
||||
| ENABLE_P REPLICA { $$ = TRIGGER_FIRES_ON_REPLICA; }
|
||||
| ENABLE_P ALWAYS { $$ = TRIGGER_FIRES_ALWAYS; }
|
||||
| DISABLE_P { $$ = TRIGGER_DISABLED; }
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUERIES :
|
||||
@ -4868,6 +4954,7 @@ drop_type: TABLE { $$ = OBJECT_TABLE; }
|
||||
| VIEW { $$ = OBJECT_VIEW; }
|
||||
| INDEX { $$ = OBJECT_INDEX; }
|
||||
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
|
||||
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
|
||||
| TYPE_P { $$ = OBJECT_TYPE; }
|
||||
| DOMAIN_P { $$ = OBJECT_DOMAIN; }
|
||||
| COLLATION { $$ = OBJECT_COLLATION; }
|
||||
@ -4931,7 +5018,7 @@ opt_restart_seqs:
|
||||
* EXTENSION | ROLE | TEXT SEARCH PARSER |
|
||||
* TEXT SEARCH DICTIONARY | TEXT SEARCH TEMPLATE |
|
||||
* TEXT SEARCH CONFIGURATION | FOREIGN TABLE |
|
||||
* FOREIGN DATA WRAPPER | SERVER ] <objname> |
|
||||
* FOREIGN DATA WRAPPER | SERVER | EVENT TRIGGER ] <objname> |
|
||||
* AGGREGATE <aggname> (arg1, ...) |
|
||||
* FUNCTION <funcname> (arg1, arg2, ...) |
|
||||
* OPERATOR <op> (leftoperand_typ, rightoperand_typ) |
|
||||
@ -5113,6 +5200,7 @@ comment_type:
|
||||
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
|
||||
| SERVER { $$ = OBJECT_FOREIGN_SERVER; }
|
||||
| FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; }
|
||||
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
|
||||
;
|
||||
|
||||
comment_text:
|
||||
@ -5195,6 +5283,7 @@ opt_provider: FOR ColId_or_Sconst { $$ = $2; }
|
||||
security_label_type:
|
||||
COLUMN { $$ = OBJECT_COLUMN; }
|
||||
| DATABASE { $$ = OBJECT_DATABASE; }
|
||||
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
|
||||
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
|
||||
| SCHEMA { $$ = OBJECT_SCHEMA; }
|
||||
| SEQUENCE { $$ = OBJECT_SEQUENCE; }
|
||||
@ -6850,6 +6939,14 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
|
||||
n->missing_ok = false;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| ALTER EVENT TRIGGER name RENAME TO name
|
||||
{
|
||||
RenameStmt *n = makeNode(RenameStmt);
|
||||
n->renameType = OBJECT_EVENT_TRIGGER;
|
||||
n->subname = $4;
|
||||
n->newname = $7;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| ALTER ROLE RoleId RENAME TO RoleId
|
||||
{
|
||||
RenameStmt *n = makeNode(RenameStmt);
|
||||
@ -7329,6 +7426,14 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId
|
||||
n->newowner = $6;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| ALTER EVENT TRIGGER name OWNER TO RoleId
|
||||
{
|
||||
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
||||
n->objectType = OBJECT_EVENT_TRIGGER;
|
||||
n->object = list_make1(makeString($4));
|
||||
n->newowner = $7;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "commands/dbcommands.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "commands/discard.h"
|
||||
#include "commands/event_trigger.h"
|
||||
#include "commands/explain.h"
|
||||
#include "commands/extension.h"
|
||||
#include "commands/lockcmds.h"
|
||||
@ -183,6 +184,8 @@ check_xact_readonly(Node *parsetree)
|
||||
case T_CommentStmt:
|
||||
case T_DefineStmt:
|
||||
case T_CreateCastStmt:
|
||||
case T_CreateEventTrigStmt:
|
||||
case T_AlterEventTrigStmt:
|
||||
case T_CreateConversionStmt:
|
||||
case T_CreatedbStmt:
|
||||
case T_CreateDomainStmt:
|
||||
@ -1056,6 +1059,14 @@ standard_ProcessUtility(Node *parsetree,
|
||||
InvalidOid, InvalidOid, false);
|
||||
break;
|
||||
|
||||
case T_CreateEventTrigStmt:
|
||||
CreateEventTrigger((CreateEventTrigStmt *) parsetree);
|
||||
break;
|
||||
|
||||
case T_AlterEventTrigStmt:
|
||||
AlterEventTrigger((AlterEventTrigStmt *) parsetree);
|
||||
break;
|
||||
|
||||
case T_CreatePLangStmt:
|
||||
CreateProceduralLanguage((CreatePLangStmt *) parsetree);
|
||||
break;
|
||||
@ -1472,6 +1483,9 @@ AlterObjectTypeCommandTag(ObjectType objtype)
|
||||
case OBJECT_TRIGGER:
|
||||
tag = "ALTER TRIGGER";
|
||||
break;
|
||||
case OBJECT_EVENT_TRIGGER:
|
||||
tag = "ALTER EVENT TRIGGER";
|
||||
break;
|
||||
case OBJECT_TSCONFIGURATION:
|
||||
tag = "ALTER TEXT SEARCH CONFIGURATION";
|
||||
break;
|
||||
@ -1741,6 +1755,9 @@ CreateCommandTag(Node *parsetree)
|
||||
case OBJECT_TRIGGER:
|
||||
tag = "DROP TRIGGER";
|
||||
break;
|
||||
case OBJECT_EVENT_TRIGGER:
|
||||
tag = "DROP EVENT TRIGGER";
|
||||
break;
|
||||
case OBJECT_RULE:
|
||||
tag = "DROP RULE";
|
||||
break;
|
||||
@ -1994,6 +2011,14 @@ CreateCommandTag(Node *parsetree)
|
||||
tag = "CREATE TRIGGER";
|
||||
break;
|
||||
|
||||
case T_CreateEventTrigStmt:
|
||||
tag = "CREATE EVENT TRIGGER";
|
||||
break;
|
||||
|
||||
case T_AlterEventTrigStmt:
|
||||
tag = "ALTER EVENT TRIGGER";
|
||||
break;
|
||||
|
||||
case T_CreatePLangStmt:
|
||||
tag = "CREATE LANGUAGE";
|
||||
break;
|
||||
@ -2489,6 +2514,14 @@ GetCommandLogLevel(Node *parsetree)
|
||||
lev = LOGSTMT_DDL;
|
||||
break;
|
||||
|
||||
case T_CreateEventTrigStmt:
|
||||
lev = LOGSTMT_DDL;
|
||||
break;
|
||||
|
||||
case T_AlterEventTrigStmt:
|
||||
lev = LOGSTMT_DDL;
|
||||
break;
|
||||
|
||||
case T_CreatePLangStmt:
|
||||
lev = LOGSTMT_DDL;
|
||||
break;
|
||||
|
@ -292,6 +292,33 @@ trigger_out(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* event_trigger_in - input routine for pseudo-type event_trigger.
|
||||
*/
|
||||
Datum
|
||||
event_trigger_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot accept a value of type event_trigger")));
|
||||
|
||||
PG_RETURN_VOID(); /* keep compiler quiet */
|
||||
}
|
||||
|
||||
/*
|
||||
* event_trigger_out - output routine for pseudo-type event_trigger.
|
||||
*/
|
||||
Datum
|
||||
event_trigger_out(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot display a value of type event_trigger")));
|
||||
|
||||
PG_RETURN_VOID(); /* keep compiler quiet */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* language_handler_in - input routine for pseudo-type LANGUAGE_HANDLER.
|
||||
*/
|
||||
|
23
src/backend/utils/cache/syscache.c
vendored
23
src/backend/utils/cache/syscache.c
vendored
@ -34,6 +34,7 @@
|
||||
#include "catalog/pg_database.h"
|
||||
#include "catalog/pg_default_acl.h"
|
||||
#include "catalog/pg_enum.h"
|
||||
#include "catalog/pg_event_trigger.h"
|
||||
#include "catalog/pg_foreign_data_wrapper.h"
|
||||
#include "catalog/pg_foreign_server.h"
|
||||
#include "catalog/pg_foreign_table.h"
|
||||
@ -379,6 +380,28 @@ static const struct cachedesc cacheinfo[] = {
|
||||
},
|
||||
256
|
||||
},
|
||||
{EventTriggerRelationId, /* EVENTTRIGGERNAME */
|
||||
EventTriggerNameIndexId,
|
||||
1,
|
||||
{
|
||||
Anum_pg_event_trigger_evtname,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
8
|
||||
},
|
||||
{EventTriggerRelationId, /* EVENTTRIGGEROID */
|
||||
EventTriggerOidIndexId,
|
||||
1,
|
||||
{
|
||||
ObjectIdAttributeNumber,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
8
|
||||
},
|
||||
{ForeignDataWrapperRelationId, /* FOREIGNDATAWRAPPERNAME */
|
||||
ForeignDataWrapperNameIndexId,
|
||||
1,
|
||||
|
@ -100,6 +100,7 @@ getSchemaData(Archive *fout, int *numTablesPtr)
|
||||
int numForeignDataWrappers;
|
||||
int numForeignServers;
|
||||
int numDefaultACLs;
|
||||
int numEventTriggers;
|
||||
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "reading schemas\n");
|
||||
@ -240,6 +241,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
|
||||
write_msg(NULL, "reading triggers\n");
|
||||
getTriggers(fout, tblinfo, numTables);
|
||||
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "reading event triggers\n");
|
||||
getEventTriggers(fout, &numEventTriggers);
|
||||
|
||||
*numTablesPtr = numTables;
|
||||
return tblinfo;
|
||||
}
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "catalog/pg_cast.h"
|
||||
#include "catalog/pg_class.h"
|
||||
#include "catalog/pg_default_acl.h"
|
||||
#include "catalog/pg_event_trigger.h"
|
||||
#include "catalog/pg_largeobject.h"
|
||||
#include "catalog/pg_largeobject_metadata.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
@ -186,6 +187,7 @@ static void dumpConversion(Archive *fout, ConvInfo *convinfo);
|
||||
static void dumpRule(Archive *fout, RuleInfo *rinfo);
|
||||
static void dumpAgg(Archive *fout, AggInfo *agginfo);
|
||||
static void dumpTrigger(Archive *fout, TriggerInfo *tginfo);
|
||||
static void dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo);
|
||||
static void dumpTable(Archive *fout, TableInfo *tbinfo);
|
||||
static void dumpTableSchema(Archive *fout, TableInfo *tbinfo);
|
||||
static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo);
|
||||
@ -5296,6 +5298,87 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
|
||||
destroyPQExpBuffer(query);
|
||||
}
|
||||
|
||||
/*
|
||||
* getEventTriggers
|
||||
* get information about event triggers
|
||||
*/
|
||||
EventTriggerInfo *
|
||||
getEventTriggers(Archive *fout, int *numEventTriggers)
|
||||
{
|
||||
int i;
|
||||
PQExpBuffer query = createPQExpBuffer();
|
||||
PGresult *res;
|
||||
EventTriggerInfo *evtinfo;
|
||||
int i_tableoid,
|
||||
i_oid,
|
||||
i_evtname,
|
||||
i_evtevent,
|
||||
i_evtowner,
|
||||
i_evttags,
|
||||
i_evtfname,
|
||||
i_evtenabled;
|
||||
int ntups;
|
||||
|
||||
/* Before 9.3, there are no event triggers */
|
||||
if (fout->remoteVersion < 90300)
|
||||
{
|
||||
*numEventTriggers = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Make sure we are in proper schema */
|
||||
selectSourceSchema(fout, "pg_catalog");
|
||||
|
||||
appendPQExpBuffer(query,
|
||||
"SELECT e.tableoid, e.oid, evtname, evtenabled, "
|
||||
"evtevent, (%s evtowner) AS evtowner, "
|
||||
"array_to_string(array("
|
||||
"select quote_literal(x) "
|
||||
" from unnest(evttags) as t(x)), ', ') as evttags, "
|
||||
"e.evtfoid::regproc as evtfname "
|
||||
"FROM pg_event_trigger e "
|
||||
"ORDER BY e.oid",
|
||||
username_subquery);
|
||||
|
||||
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
|
||||
|
||||
ntups = PQntuples(res);
|
||||
|
||||
*numEventTriggers = ntups;
|
||||
|
||||
evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
|
||||
|
||||
i_tableoid = PQfnumber(res, "tableoid");
|
||||
i_oid = PQfnumber(res, "oid");
|
||||
i_evtname = PQfnumber(res, "evtname");
|
||||
i_evtevent = PQfnumber(res, "evtevent");
|
||||
i_evtowner = PQfnumber(res, "evtowner");
|
||||
i_evttags = PQfnumber(res, "evttags");
|
||||
i_evtfname = PQfnumber(res, "evtfname");
|
||||
i_evtenabled = PQfnumber(res, "evtenabled");
|
||||
|
||||
for (i = 0; i < ntups; i++)
|
||||
{
|
||||
evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
|
||||
evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
|
||||
evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
|
||||
AssignDumpId(&evtinfo[i].dobj);
|
||||
evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
|
||||
evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
|
||||
evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
|
||||
evtinfo[i].evtowner = pg_strdup(PQgetvalue(res, i, i_evtowner));
|
||||
evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
|
||||
evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
|
||||
evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
|
||||
}
|
||||
|
||||
PQclear(res);
|
||||
|
||||
destroyPQExpBuffer(query);
|
||||
|
||||
return evtinfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* getProcLangs
|
||||
* get basic information about every procedural language in the system
|
||||
@ -7166,6 +7249,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
|
||||
case DO_TRIGGER:
|
||||
dumpTrigger(fout, (TriggerInfo *) dobj);
|
||||
break;
|
||||
case DO_EVENT_TRIGGER:
|
||||
dumpEventTrigger(fout, (EventTriggerInfo *) dobj);
|
||||
break;
|
||||
case DO_CONSTRAINT:
|
||||
dumpConstraint(fout, (ConstraintInfo *) dobj);
|
||||
break;
|
||||
@ -13658,6 +13744,69 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
|
||||
destroyPQExpBuffer(labelq);
|
||||
}
|
||||
|
||||
static void
|
||||
dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo)
|
||||
{
|
||||
PQExpBuffer query;
|
||||
PQExpBuffer labelq;
|
||||
|
||||
query = createPQExpBuffer();
|
||||
labelq = createPQExpBuffer();
|
||||
|
||||
appendPQExpBuffer(query, "CREATE EVENT TRIGGER ");
|
||||
appendPQExpBufferStr(query, fmtId(evtinfo->dobj.name));
|
||||
appendPQExpBuffer(query, " ON ");
|
||||
appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
|
||||
appendPQExpBufferStr(query, " ");
|
||||
|
||||
if (strcmp("", evtinfo->evttags) != 0)
|
||||
{
|
||||
appendPQExpBufferStr(query, "\n WHEN TAG IN (");
|
||||
appendPQExpBufferStr(query, evtinfo->evttags);
|
||||
appendPQExpBufferStr(query, ") ");
|
||||
}
|
||||
|
||||
appendPQExpBuffer(query, "\n EXECUTE PROCEDURE ");
|
||||
appendPQExpBufferStr(query, evtinfo->evtfname);
|
||||
appendPQExpBuffer(query, "();\n");
|
||||
|
||||
if (evtinfo->evtenabled != 'O')
|
||||
{
|
||||
appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
|
||||
fmtId(evtinfo->dobj.name));
|
||||
switch (evtinfo->evtenabled)
|
||||
{
|
||||
case 'D':
|
||||
appendPQExpBuffer(query, "DISABLE");
|
||||
break;
|
||||
case 'A':
|
||||
appendPQExpBuffer(query, "ENABLE ALWAYS");
|
||||
break;
|
||||
case 'R':
|
||||
appendPQExpBuffer(query, "ENABLE REPLICA");
|
||||
break;
|
||||
default:
|
||||
appendPQExpBuffer(query, "ENABLE");
|
||||
break;
|
||||
}
|
||||
appendPQExpBuffer(query, ";\n");
|
||||
}
|
||||
appendPQExpBuffer(labelq, "EVENT TRIGGER %s ",
|
||||
fmtId(evtinfo->dobj.name));
|
||||
|
||||
ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
|
||||
evtinfo->dobj.name, NULL, NULL, evtinfo->evtowner, false,
|
||||
"EVENT TRIGGER", SECTION_POST_DATA,
|
||||
query->data, "", NULL, NULL, 0, NULL, NULL);
|
||||
|
||||
dumpComment(fout, labelq->data,
|
||||
NULL, NULL,
|
||||
evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
|
||||
|
||||
destroyPQExpBuffer(query);
|
||||
destroyPQExpBuffer(labelq);
|
||||
}
|
||||
|
||||
/*
|
||||
* dumpRule
|
||||
* Dump a rule
|
||||
@ -14153,6 +14302,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
|
||||
break;
|
||||
case DO_INDEX:
|
||||
case DO_TRIGGER:
|
||||
case DO_EVENT_TRIGGER:
|
||||
case DO_DEFAULT_ACL:
|
||||
/* Post-data objects: must come after the post-data boundary */
|
||||
addObjectDependency(dobj, postDataBound->dumpId);
|
||||
|
@ -120,7 +120,8 @@ typedef enum
|
||||
DO_BLOB,
|
||||
DO_BLOB_DATA,
|
||||
DO_PRE_DATA_BOUNDARY,
|
||||
DO_POST_DATA_BOUNDARY
|
||||
DO_POST_DATA_BOUNDARY,
|
||||
DO_EVENT_TRIGGER
|
||||
} DumpableObjectType;
|
||||
|
||||
typedef struct _dumpableObject
|
||||
@ -352,6 +353,18 @@ typedef struct _triggerInfo
|
||||
char *tgdef;
|
||||
} TriggerInfo;
|
||||
|
||||
typedef struct _evttriggerInfo
|
||||
{
|
||||
DumpableObject dobj;
|
||||
char *evtname;
|
||||
char *evtevent;
|
||||
char *evtowner;
|
||||
char *evttags;
|
||||
char *evtfname;
|
||||
char evttype;
|
||||
char evtenabled;
|
||||
} EventTriggerInfo;
|
||||
|
||||
/*
|
||||
* struct ConstraintInfo is used for all constraint types. However we
|
||||
* use a different objType for foreign key constraints, to make it easier
|
||||
@ -562,5 +575,6 @@ extern ForeignServerInfo *getForeignServers(Archive *fout,
|
||||
extern DefaultACLInfo *getDefaultACLs(Archive *fout, int *numDefaultACLs);
|
||||
extern void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
|
||||
int numExtensions);
|
||||
extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
|
||||
|
||||
#endif /* PG_DUMP_H */
|
||||
|
@ -24,8 +24,9 @@ static const char *modulename = gettext_noop("sorter");
|
||||
* Objects are sorted by priority levels, and within an equal priority level
|
||||
* by OID. (This is a relatively crude hack to provide semi-reasonable
|
||||
* behavior for old databases without full dependency info.) Note: collations,
|
||||
* extensions, text search, foreign-data, and default ACL objects can't really
|
||||
* happen here, so the rather bogus priorities for them don't matter.
|
||||
* extensions, text search, foreign-data, event trigger, and default ACL
|
||||
* objects can't really happen here, so the rather bogus priorities for them
|
||||
* don't matter.
|
||||
*
|
||||
* NOTE: object-type priorities must match the section assignments made in
|
||||
* pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
|
||||
@ -66,7 +67,8 @@ static const int oldObjectTypePriority[] =
|
||||
9, /* DO_BLOB */
|
||||
12, /* DO_BLOB_DATA */
|
||||
10, /* DO_PRE_DATA_BOUNDARY */
|
||||
13 /* DO_POST_DATA_BOUNDARY */
|
||||
13, /* DO_POST_DATA_BOUNDARY */
|
||||
20 /* DO_EVENT_TRIGGER */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -112,7 +114,8 @@ static const int newObjectTypePriority[] =
|
||||
21, /* DO_BLOB */
|
||||
24, /* DO_BLOB_DATA */
|
||||
22, /* DO_PRE_DATA_BOUNDARY */
|
||||
25 /* DO_POST_DATA_BOUNDARY */
|
||||
25, /* DO_POST_DATA_BOUNDARY */
|
||||
32 /* DO_EVENT_TRIGGER */
|
||||
};
|
||||
|
||||
static DumpId preDataBoundId;
|
||||
@ -1147,6 +1150,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
|
||||
"TRIGGER %s (ID %d OID %u)",
|
||||
obj->name, obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_EVENT_TRIGGER:
|
||||
snprintf(buf, bufsize,
|
||||
"EVENT TRIGGER %s (ID %d OID %u)",
|
||||
obj->name, obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_CONSTRAINT:
|
||||
snprintf(buf, bufsize,
|
||||
"CONSTRAINT %s (ID %d OID %u)",
|
||||
|
@ -490,6 +490,9 @@ exec_command(const char *cmd,
|
||||
else
|
||||
success = listExtensions(pattern);
|
||||
break;
|
||||
case 'y': /* Event Triggers */
|
||||
success = listEventTriggers(pattern, show_verbose);
|
||||
break;
|
||||
default:
|
||||
status = PSQL_CMD_UNKNOWN;
|
||||
}
|
||||
|
@ -2952,6 +2952,67 @@ listConversions(const char *pattern, bool verbose, bool showSystem)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* \dy
|
||||
*
|
||||
* Describes Event Triggers.
|
||||
*/
|
||||
bool
|
||||
listEventTriggers(const char *pattern, bool verbose)
|
||||
{
|
||||
PQExpBufferData buf;
|
||||
PGresult *res;
|
||||
printQueryOpt myopt = pset.popt;
|
||||
static const bool translate_columns[] =
|
||||
{false, false, false, true, false, false, false};
|
||||
|
||||
initPQExpBuffer(&buf);
|
||||
|
||||
printfPQExpBuffer(&buf,
|
||||
"select evtname as \"%s\", "
|
||||
"evtevent as \"%s\", "
|
||||
"pg_catalog.pg_get_userbyid(e.evtowner) AS \"%s\", "
|
||||
"case evtenabled when 'O' then 'enabled' "
|
||||
" when 'R' then 'replica' "
|
||||
" when 'A' then 'always' "
|
||||
" when 'D' then 'disabled' end as \"%s\", "
|
||||
"e.evtfoid::regproc as \"%s\", "
|
||||
"array_to_string(array(select x "
|
||||
" from unnest(evttags) as t(x)), ', ') as \"%s\" ",
|
||||
gettext_noop("Name"),
|
||||
gettext_noop("Event"),
|
||||
gettext_noop("Owner"),
|
||||
gettext_noop("Enabled"),
|
||||
gettext_noop("Procedure"),
|
||||
gettext_noop("Tags"));
|
||||
if (verbose)
|
||||
appendPQExpBuffer(&buf,
|
||||
",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"%s\"",
|
||||
gettext_noop("Description"));
|
||||
appendPQExpBuffer(&buf,
|
||||
"\nFROM pg_event_trigger e ");
|
||||
|
||||
processSQLNamePattern(pset.db, &buf, pattern, false, false,
|
||||
NULL, "evtname", NULL, NULL);
|
||||
|
||||
appendPQExpBuffer(&buf, "ORDER BY 1");
|
||||
|
||||
res = PSQLexec(buf.data, false);
|
||||
termPQExpBuffer(&buf);
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
myopt.nullPrint = NULL;
|
||||
myopt.title = _("List of event triggers");
|
||||
myopt.translate_header = true;
|
||||
myopt.translate_columns = translate_columns;
|
||||
|
||||
printQuery(res, &myopt, pset.queryFout, pset.logfile);
|
||||
|
||||
PQclear(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* \dC
|
||||
*
|
||||
|
@ -96,4 +96,7 @@ extern bool listExtensions(const char *pattern);
|
||||
/* \dx+ */
|
||||
extern bool listExtensionContents(const char *pattern);
|
||||
|
||||
/* \dy */
|
||||
extern bool listEventTriggers(const char *pattern, bool verbose);
|
||||
|
||||
#endif /* DESCRIBE_H */
|
||||
|
@ -229,6 +229,7 @@ slashUsage(unsigned short int pager)
|
||||
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
|
||||
fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n"));
|
||||
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
|
||||
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
|
||||
fprintf(output, _(" \\l[+] list all databases\n"));
|
||||
fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n"));
|
||||
fprintf(output, _(" \\z [PATTERN] same as \\dp\n"));
|
||||
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201207111
|
||||
#define CATALOG_VERSION_NO 201207181
|
||||
|
||||
#endif
|
||||
|
@ -146,6 +146,7 @@ typedef enum ObjectClass
|
||||
OCLASS_USER_MAPPING, /* pg_user_mapping */
|
||||
OCLASS_DEFACL, /* pg_default_acl */
|
||||
OCLASS_EXTENSION, /* pg_extension */
|
||||
OCLASS_EVENT_TRIGGER, /* pg_event_trigger */
|
||||
MAX_OCLASS /* MUST BE LAST */
|
||||
} ObjectClass;
|
||||
|
||||
|
@ -234,6 +234,11 @@ DECLARE_UNIQUE_INDEX(pg_trigger_tgrelid_tgname_index, 2701, on pg_trigger using
|
||||
DECLARE_UNIQUE_INDEX(pg_trigger_oid_index, 2702, on pg_trigger using btree(oid oid_ops));
|
||||
#define TriggerOidIndexId 2702
|
||||
|
||||
DECLARE_UNIQUE_INDEX(pg_event_trigger_evtname_index, 3467, on pg_event_trigger using btree(evtname name_ops));
|
||||
#define EventTriggerNameIndexId 3467
|
||||
DECLARE_UNIQUE_INDEX(pg_event_trigger_oid_index, 3468, on pg_event_trigger using btree(oid oid_ops));
|
||||
#define EventTriggerOidIndexId 3468
|
||||
|
||||
DECLARE_UNIQUE_INDEX(pg_ts_config_cfgname_index, 3608, on pg_ts_config using btree(cfgname name_ops, cfgnamespace oid_ops));
|
||||
#define TSConfigNameNspIndexId 3608
|
||||
DECLARE_UNIQUE_INDEX(pg_ts_config_oid_index, 3712, on pg_ts_config using btree(oid oid_ops));
|
||||
|
63
src/include/catalog/pg_event_trigger.h
Normal file
63
src/include/catalog/pg_event_trigger.h
Normal file
@ -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 */
|
@ -3460,6 +3460,10 @@ DATA(insert OID = 2300 ( trigger_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2
|
||||
DESCR("I/O");
|
||||
DATA(insert OID = 2301 ( trigger_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "2279" _null_ _null_ _null_ _null_ trigger_out _null_ _null_ _null_ ));
|
||||
DESCR("I/O");
|
||||
DATA(insert OID = 3594 ( event_trigger_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 3838 "2275" _null_ _null_ _null_ _null_ event_trigger_in _null_ _null_ _null_ ));
|
||||
DESCR("I/O");
|
||||
DATA(insert OID = 3595 ( event_trigger_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3838" _null_ _null_ _null_ _null_ event_trigger_out _null_ _null_ _null_ ));
|
||||
DESCR("I/O");
|
||||
DATA(insert OID = 2302 ( language_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2280 "2275" _null_ _null_ _null_ _null_ language_handler_in _null_ _null_ _null_ ));
|
||||
DESCR("I/O");
|
||||
DATA(insert OID = 2303 ( language_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "2280" _null_ _null_ _null_ _null_ language_handler_out _null_ _null_ _null_ ));
|
||||
|
@ -650,6 +650,8 @@ DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void
|
||||
#define VOIDOID 2278
|
||||
DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
|
||||
#define TRIGGEROID 2279
|
||||
DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
|
||||
#define EVTTRIGGEROID 3838
|
||||
DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
|
||||
#define LANGUAGE_HANDLEROID 2280
|
||||
DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
|
||||
|
28
src/include/commands/event_trigger.h
Normal file
28
src/include/commands/event_trigger.h
Normal file
@ -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 */
|
@ -357,6 +357,8 @@ typedef enum NodeTag
|
||||
T_CreateExtensionStmt,
|
||||
T_AlterExtensionStmt,
|
||||
T_AlterExtensionContentsStmt,
|
||||
T_CreateEventTrigStmt,
|
||||
T_AlterEventTrigStmt,
|
||||
|
||||
/*
|
||||
* TAGS FOR PARSE TREE NODES (parsenodes.h)
|
||||
|
@ -1113,6 +1113,7 @@ typedef enum ObjectType
|
||||
OBJECT_CONVERSION,
|
||||
OBJECT_DATABASE,
|
||||
OBJECT_DOMAIN,
|
||||
OBJECT_EVENT_TRIGGER,
|
||||
OBJECT_EXTENSION,
|
||||
OBJECT_FDW,
|
||||
OBJECT_FOREIGN_SERVER,
|
||||
@ -1731,6 +1732,32 @@ typedef struct CreateTrigStmt
|
||||
} CreateTrigStmt;
|
||||
|
||||
/* ----------------------
|
||||
* Create EVENT TRIGGER Statement
|
||||
* ----------------------
|
||||
*/
|
||||
typedef struct CreateEventTrigStmt
|
||||
{
|
||||
NodeTag type;
|
||||
char *trigname; /* TRIGGER's name */
|
||||
char *eventname; /* event's identifier */
|
||||
List *whenclause; /* list of DefElems indicating filtering */
|
||||
List *funcname; /* qual. name of function to call */
|
||||
} CreateEventTrigStmt;
|
||||
|
||||
/* ----------------------
|
||||
* Alter EVENT TRIGGER Statement
|
||||
* ----------------------
|
||||
*/
|
||||
typedef struct AlterEventTrigStmt
|
||||
{
|
||||
NodeTag type;
|
||||
char *trigname; /* TRIGGER's name */
|
||||
char tgenabled; /* trigger's firing configuration WRT
|
||||
* session_replication_role */
|
||||
} AlterEventTrigStmt;
|
||||
|
||||
/* ----------------------
|
||||
* Create/Drop PROCEDURAL LANGUAGE Statements
|
||||
* Create PROCEDURAL LANGUAGE Statements
|
||||
* ----------------------
|
||||
*/
|
||||
|
@ -141,6 +141,7 @@ PG_KEYWORD("encrypted", ENCRYPTED, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("end", END_P, RESERVED_KEYWORD)
|
||||
PG_KEYWORD("enum", ENUM_P, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("escape", ESCAPE, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("event", EVENT, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("except", EXCEPT, RESERVED_KEYWORD)
|
||||
PG_KEYWORD("exclude", EXCLUDE, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("excluding", EXCLUDING, UNRESERVED_KEYWORD)
|
||||
|
@ -195,6 +195,7 @@ typedef enum AclObjectKind
|
||||
ACL_KIND_TSCONFIGURATION, /* pg_ts_config */
|
||||
ACL_KIND_FDW, /* pg_foreign_data_wrapper */
|
||||
ACL_KIND_FOREIGN_SERVER, /* pg_foreign_server */
|
||||
ACL_KIND_EVENT_TRIGGER, /* pg_event_trigger */
|
||||
ACL_KIND_EXTENSION, /* pg_extension */
|
||||
MAX_ACL_KIND /* MUST BE LAST */
|
||||
} AclObjectKind;
|
||||
@ -322,6 +323,7 @@ extern bool pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid);
|
||||
extern bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid);
|
||||
extern bool pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid);
|
||||
extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid);
|
||||
extern bool pg_event_trigger_ownercheck(Oid et_oid, Oid roleid);
|
||||
extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
|
||||
extern bool has_createrole_privilege(Oid roleid);
|
||||
|
||||
|
@ -532,6 +532,8 @@ extern Datum void_recv(PG_FUNCTION_ARGS);
|
||||
extern Datum void_send(PG_FUNCTION_ARGS);
|
||||
extern Datum trigger_in(PG_FUNCTION_ARGS);
|
||||
extern Datum trigger_out(PG_FUNCTION_ARGS);
|
||||
extern Datum event_trigger_in(PG_FUNCTION_ARGS);
|
||||
extern Datum event_trigger_out(PG_FUNCTION_ARGS);
|
||||
extern Datum language_handler_in(PG_FUNCTION_ARGS);
|
||||
extern Datum language_handler_out(PG_FUNCTION_ARGS);
|
||||
extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
|
||||
|
@ -54,6 +54,8 @@ enum SysCacheIdentifier
|
||||
DEFACLROLENSPOBJ,
|
||||
ENUMOID,
|
||||
ENUMTYPOIDNAME,
|
||||
EVENTTRIGGERNAME,
|
||||
EVENTTRIGGEROID,
|
||||
FOREIGNDATAWRAPPERNAME,
|
||||
FOREIGNDATAWRAPPEROID,
|
||||
FOREIGNSERVERNAME,
|
||||
|
90
src/test/regress/expected/event_trigger.out
Normal file
90
src/test/regress/expected/event_trigger.out
Normal file
@ -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;
|
@ -1276,8 +1276,8 @@ drop table cchild;
|
||||
-- Check that ruleutils are working
|
||||
--
|
||||
SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname;
|
||||
viewname | definition
|
||||
---------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
viewname | definition
|
||||
---------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath);
|
||||
pg_available_extension_versions | SELECT e.name, e.version, (x.extname IS NOT NULL) AS installed, e.superuser, e.relocatable, e.schema, e.requires, e.comment FROM (pg_available_extension_versions() e(name, version, superuser, relocatable, schema, requires, comment) LEFT JOIN pg_extension x ON (((e.name = x.extname) AND (e.version = x.extversion))));
|
||||
pg_available_extensions | SELECT e.name, e.default_version, x.extversion AS installed_version, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname)));
|
||||
@ -1289,7 +1289,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
|
||||
pg_prepared_xacts | SELECT p.transaction, p.gid, p.prepared, u.rolname AS owner, d.datname AS database FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid) LEFT JOIN pg_authid u ON ((p.ownerid = u.oid))) LEFT JOIN pg_database d ON ((p.dbid = d.oid)));
|
||||
pg_roles | SELECT pg_authid.rolname, pg_authid.rolsuper, pg_authid.rolinherit, pg_authid.rolcreaterole, pg_authid.rolcreatedb, pg_authid.rolcatupdate, pg_authid.rolcanlogin, pg_authid.rolreplication, pg_authid.rolconnlimit, '********'::text AS rolpassword, pg_authid.rolvaliduntil, s.setconfig AS rolconfig, pg_authid.oid FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid))));
|
||||
pg_rules | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name);
|
||||
pg_seclabels | ((((((((SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (rel.relkind = 'r'::"char") THEN 'table'::text WHEN (rel.relkind = 'v'::"char") THEN 'view'::text WHEN (rel.relkind = 'S'::"char") THEN 'sequence'::text WHEN (rel.relkind = 'f'::"char") THEN 'foreign table'::text ELSE NULL::text END AS objtype, rel.relnamespace AS objnamespace, CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'column'::text AS objtype, rel.relnamespace AS objnamespace, ((CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END || '.'::text) || (att.attname)::text) AS objname, l.provider, l.label FROM (((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid <> 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (pro.proisagg = true) THEN 'aggregate'::text WHEN (pro.proisagg = false) THEN 'function'::text ELSE NULL::text END AS objtype, pro.pronamespace AS objnamespace, (((CASE WHEN pg_function_is_visible(pro.oid) THEN quote_ident((pro.proname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((pro.proname)::text)) END || '('::text) || pg_get_function_arguments(pro.oid)) || ')'::text) AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (typ.typtype = 'd'::"char") THEN 'domain'::text ELSE 'type'::text END AS objtype, typ.typnamespace AS objnamespace, CASE WHEN pg_type_is_visible(typ.oid) THEN quote_ident((typ.typname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((typ.typname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'large object'::text AS objtype, NULL::oid AS objnamespace, (l.objoid)::text AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) WHERE ((l.classoid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0))) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'language'::text AS objtype, NULL::oid AS objnamespace, quote_ident((lan.lanname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'schema'::text AS objtype, nsp.oid AS objnamespace, quote_ident((nsp.nspname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, 0 AS objsubid, 'database'::text AS objtype, NULL::oid AS objnamespace, quote_ident((dat.datname)::text) AS objname, l.provider, l.label FROM (pg_shseclabel l JOIN pg_database dat ON (((l.classoid = dat.tableoid) AND (l.objoid = dat.oid))))) UNION ALL SELECT l.objoid, l.classoid, 0 AS objsubid, 'tablespace'::text AS objtype, NULL::oid AS objnamespace, quote_ident((spc.spcname)::text) AS objname, l.provider, l.label FROM (pg_shseclabel l JOIN pg_tablespace spc ON (((l.classoid = spc.tableoid) AND (l.objoid = spc.oid))))) UNION ALL SELECT l.objoid, l.classoid, 0 AS objsubid, 'role'::text AS objtype, NULL::oid AS objnamespace, quote_ident((rol.rolname)::text) AS objname, l.provider, l.label FROM (pg_shseclabel l JOIN pg_authid rol ON (((l.classoid = rol.tableoid) AND (l.objoid = rol.oid))));
|
||||
pg_seclabels | (((((((((SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (rel.relkind = 'r'::"char") THEN 'table'::text WHEN (rel.relkind = 'v'::"char") THEN 'view'::text WHEN (rel.relkind = 'S'::"char") THEN 'sequence'::text WHEN (rel.relkind = 'f'::"char") THEN 'foreign table'::text ELSE NULL::text END AS objtype, rel.relnamespace AS objnamespace, CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'column'::text AS objtype, rel.relnamespace AS objnamespace, ((CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END || '.'::text) || (att.attname)::text) AS objname, l.provider, l.label FROM (((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid <> 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (pro.proisagg = true) THEN 'aggregate'::text WHEN (pro.proisagg = false) THEN 'function'::text ELSE NULL::text END AS objtype, pro.pronamespace AS objnamespace, (((CASE WHEN pg_function_is_visible(pro.oid) THEN quote_ident((pro.proname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((pro.proname)::text)) END || '('::text) || pg_get_function_arguments(pro.oid)) || ')'::text) AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (typ.typtype = 'd'::"char") THEN 'domain'::text ELSE 'type'::text END AS objtype, typ.typnamespace AS objnamespace, CASE WHEN pg_type_is_visible(typ.oid) THEN quote_ident((typ.typname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((typ.typname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'large object'::text AS objtype, NULL::oid AS objnamespace, (l.objoid)::text AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) WHERE ((l.classoid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0))) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'language'::text AS objtype, NULL::oid AS objnamespace, quote_ident((lan.lanname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'schema'::text AS objtype, nsp.oid AS objnamespace, quote_ident((nsp.nspname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'event trigger'::text AS objtype, NULL::oid AS objnamespace, quote_ident((evt.evtname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_event_trigger evt ON (((l.classoid = evt.tableoid) AND (l.objoid = evt.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, 0 AS objsubid, 'database'::text AS objtype, NULL::oid AS objnamespace, quote_ident((dat.datname)::text) AS objname, l.provider, l.label FROM (pg_shseclabel l JOIN pg_database dat ON (((l.classoid = dat.tableoid) AND (l.objoid = dat.oid))))) UNION ALL SELECT l.objoid, l.classoid, 0 AS objsubid, 'tablespace'::text AS objtype, NULL::oid AS objnamespace, quote_ident((spc.spcname)::text) AS objname, l.provider, l.label FROM (pg_shseclabel l JOIN pg_tablespace spc ON (((l.classoid = spc.tableoid) AND (l.objoid = spc.oid))))) UNION ALL SELECT l.objoid, l.classoid, 0 AS objsubid, 'role'::text AS objtype, NULL::oid AS objnamespace, quote_ident((rol.rolname)::text) AS objname, l.provider, l.label FROM (pg_shseclabel l JOIN pg_authid rol ON (((l.classoid = rol.tableoid) AND (l.objoid = rol.oid))));
|
||||
pg_settings | SELECT a.name, a.setting, a.unit, a.category, a.short_desc, a.extra_desc, a.context, a.vartype, a.source, a.min_val, a.max_val, a.enumvals, a.boot_val, a.reset_val, a.sourcefile, a.sourceline FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline);
|
||||
pg_shadow | SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, pg_authid.rolcreatedb AS usecreatedb, pg_authid.rolsuper AS usesuper, pg_authid.rolcatupdate AS usecatupd, pg_authid.rolreplication AS userepl, pg_authid.rolpassword AS passwd, (pg_authid.rolvaliduntil)::abstime AS valuntil, s.setconfig AS useconfig FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin;
|
||||
pg_stat_activity | SELECT s.datid, d.datname, s.pid, s.usesysid, u.rolname AS usename, s.application_name, s.client_addr, s.client_hostname, s.client_port, s.backend_start, s.xact_start, s.query_start, s.state_change, s.waiting, s.state, s.query FROM pg_database d, pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port), pg_authid u WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
|
||||
|
@ -102,6 +102,7 @@ SELECT relname, relhasindex
|
||||
pg_depend | t
|
||||
pg_description | t
|
||||
pg_enum | t
|
||||
pg_event_trigger | t
|
||||
pg_extension | t
|
||||
pg_foreign_data_wrapper | t
|
||||
pg_foreign_server | t
|
||||
@ -164,7 +165,7 @@ SELECT relname, relhasindex
|
||||
timetz_tbl | f
|
||||
tinterval_tbl | f
|
||||
varchar_tbl | f
|
||||
(153 rows)
|
||||
(154 rows)
|
||||
|
||||
--
|
||||
-- another sanity check: every system catalog that has OIDs should have
|
||||
|
@ -88,6 +88,8 @@ test: privileges security_label collate
|
||||
test: misc
|
||||
# rules cannot run concurrently with any test that creates a view
|
||||
test: rules
|
||||
# event triggers cannot run concurrently with any test that runs DDL
|
||||
test: event_trigger
|
||||
|
||||
# ----------
|
||||
# Another group of parallel tests
|
||||
|
@ -96,6 +96,7 @@ test: security_label
|
||||
test: collate
|
||||
test: misc
|
||||
test: rules
|
||||
test: event_trigger
|
||||
test: select_views
|
||||
test: portals_p2
|
||||
test: foreign_key
|
||||
|
93
src/test/regress/sql/event_trigger.sql
Normal file
93
src/test/regress/sql/event_trigger.sql
Normal file
@ -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
Block a user