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:
Robert Haas 2012-07-18 10:16:16 -04:00
parent faf26bf117
commit 3855968f32
56 changed files with 2398 additions and 18 deletions

View File

@ -143,6 +143,11 @@
<entry>enum label and value definitions</entry> <entry>enum label and value definitions</entry>
</row> </row>
<row>
<entry><link linkend="catalog-pg-event-trigger"><structname>pg_event_trigger</structname></link></entry>
<entry>event triggers</entry>
</row>
<row> <row>
<entry><link linkend="catalog-pg-extension"><structname>pg_extension</structname></link></entry> <entry><link linkend="catalog-pg-extension"><structname>pg_extension</structname></link></entry>
<entry>installed extensions</entry> <entry>installed extensions</entry>
@ -1857,6 +1862,88 @@
</para> </para>
</sect1> </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"> <sect1 id="catalog-pg-constraint">
<title><structname>pg_constraint</structname></title> <title><structname>pg_constraint</structname></title>

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

View File

@ -61,6 +61,7 @@
<!ENTITY rules SYSTEM "rules.sgml"> <!ENTITY rules SYSTEM "rules.sgml">
<!ENTITY spi SYSTEM "spi.sgml"> <!ENTITY spi SYSTEM "spi.sgml">
<!ENTITY trigger SYSTEM "trigger.sgml"> <!ENTITY trigger SYSTEM "trigger.sgml">
<!ENTITY event-trigger SYSTEM "event-trigger.sgml">
<!ENTITY xaggr SYSTEM "xaggr.sgml"> <!ENTITY xaggr SYSTEM "xaggr.sgml">
<!ENTITY xfunc SYSTEM "xfunc.sgml"> <!ENTITY xfunc SYSTEM "xfunc.sgml">
<!ENTITY xindex SYSTEM "xindex.sgml"> <!ENTITY xindex SYSTEM "xindex.sgml">

View File

@ -208,6 +208,7 @@
&extend; &extend;
&trigger; &trigger;
&event-trigger;
&rules; &rules;
&xplang; &xplang;

View File

@ -12,6 +12,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY alterDatabase SYSTEM "alter_database.sgml"> <!ENTITY alterDatabase SYSTEM "alter_database.sgml">
<!ENTITY alterDefaultPrivileges SYSTEM "alter_default_privileges.sgml"> <!ENTITY alterDefaultPrivileges SYSTEM "alter_default_privileges.sgml">
<!ENTITY alterDomain SYSTEM "alter_domain.sgml"> <!ENTITY alterDomain SYSTEM "alter_domain.sgml">
<!ENTITY alterEventTrigger SYSTEM "alter_event_trigger.sgml">
<!ENTITY alterExtension SYSTEM "alter_extension.sgml"> <!ENTITY alterExtension SYSTEM "alter_extension.sgml">
<!ENTITY alterForeignDataWrapper SYSTEM "alter_foreign_data_wrapper.sgml"> <!ENTITY alterForeignDataWrapper SYSTEM "alter_foreign_data_wrapper.sgml">
<!ENTITY alterForeignTable SYSTEM "alter_foreign_table.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 createConversion SYSTEM "create_conversion.sgml">
<!ENTITY createDatabase SYSTEM "create_database.sgml"> <!ENTITY createDatabase SYSTEM "create_database.sgml">
<!ENTITY createDomain SYSTEM "create_domain.sgml"> <!ENTITY createDomain SYSTEM "create_domain.sgml">
<!ENTITY createEventTrigger SYSTEM "create_event_trigger.sgml">
<!ENTITY createExtension SYSTEM "create_extension.sgml"> <!ENTITY createExtension SYSTEM "create_extension.sgml">
<!ENTITY createForeignDataWrapper SYSTEM "create_foreign_data_wrapper.sgml"> <!ENTITY createForeignDataWrapper SYSTEM "create_foreign_data_wrapper.sgml">
<!ENTITY createForeignTable SYSTEM "create_foreign_table.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 dropConversion SYSTEM "drop_conversion.sgml">
<!ENTITY dropDatabase SYSTEM "drop_database.sgml"> <!ENTITY dropDatabase SYSTEM "drop_database.sgml">
<!ENTITY dropDomain SYSTEM "drop_domain.sgml"> <!ENTITY dropDomain SYSTEM "drop_domain.sgml">
<!ENTITY dropEventTrigger SYSTEM "drop_event_trigger.sgml">
<!ENTITY dropExtension SYSTEM "drop_extension.sgml"> <!ENTITY dropExtension SYSTEM "drop_extension.sgml">
<!ENTITY dropForeignDataWrapper SYSTEM "drop_foreign_data_wrapper.sgml"> <!ENTITY dropForeignDataWrapper SYSTEM "drop_foreign_data_wrapper.sgml">
<!ENTITY dropForeignTable SYSTEM "drop_foreign_table.sgml"> <!ENTITY dropForeignTable SYSTEM "drop_foreign_table.sgml">

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

View File

@ -35,6 +35,7 @@ ALTER EXTENSION <replaceable class="PARAMETER">name</replaceable> DROP <replacea
COLLATION <replaceable class="PARAMETER">object_name</replaceable> | COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
CONVERSION <replaceable class="PARAMETER">object_name</replaceable> | CONVERSION <replaceable class="PARAMETER">object_name</replaceable> |
DOMAIN <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 DATA WRAPPER <replaceable class="PARAMETER">object_name</replaceable> |
FOREIGN TABLE <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> [, ...] ] ) | FUNCTION <replaceable class="PARAMETER">function_name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) |

View File

@ -32,6 +32,7 @@ COMMENT ON
DATABASE <replaceable class="PARAMETER">object_name</replaceable> | DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
DOMAIN <replaceable class="PARAMETER">object_name</replaceable> | DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
EXTENSION <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 DATA WRAPPER <replaceable class="PARAMETER">object_name</replaceable> |
FOREIGN TABLE <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> [, ...] ] ) | FUNCTION <replaceable class="PARAMETER">function_name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) |

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

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

View File

@ -1455,6 +1455,20 @@ testdb=&gt;
</listitem> </listitem>
</varlistentry> </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> <varlistentry>
<term><literal>\e</literal> or <literal>\edit</> <literal> <optional> <replaceable class="parameter">filename</> </optional> <optional> <replaceable class="parameter">line_number</> </optional> </literal></term> <term><literal>\e</literal> or <literal>\edit</> <literal> <optional> <replaceable class="parameter">filename</> </optional> <optional> <replaceable class="parameter">line_number</> </optional> </literal></term>

View File

@ -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> [, ...] ) | AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable> [, ...] ) |
DATABASE <replaceable class="PARAMETER">object_name</replaceable> | DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
DOMAIN <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> 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> [, ...] ] ) | 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> | LARGE OBJECT <replaceable class="PARAMETER">large_object_oid</replaceable> |

View File

@ -41,6 +41,7 @@
&alterDefaultPrivileges; &alterDefaultPrivileges;
&alterDomain; &alterDomain;
&alterExtension; &alterExtension;
&alterEventTrigger;
&alterForeignDataWrapper; &alterForeignDataWrapper;
&alterForeignTable; &alterForeignTable;
&alterFunction; &alterFunction;
@ -82,6 +83,7 @@
&createDatabase; &createDatabase;
&createDomain; &createDomain;
&createExtension; &createExtension;
&createEventTrigger;
&createForeignDataWrapper; &createForeignDataWrapper;
&createForeignTable; &createForeignTable;
&createFunction; &createFunction;
@ -120,6 +122,7 @@
&dropDatabase; &dropDatabase;
&dropDomain; &dropDomain;
&dropExtension; &dropExtension;
&dropEventTrigger;
&dropForeignDataWrapper; &dropForeignDataWrapper;
&dropForeignTable; &dropForeignTable;
&dropFunction; &dropFunction;

View File

@ -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_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_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_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_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_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 \ pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \

View File

@ -29,6 +29,7 @@
#include "catalog/pg_conversion.h" #include "catalog/pg_conversion.h"
#include "catalog/pg_database.h" #include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h" #include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_extension.h" #include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.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: case ACL_KIND_FOREIGN_SERVER:
whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER; whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
break; 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: case ACL_KIND_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE; whole_mask = ACL_ALL_RIGHTS_TYPE;
break; break;
@ -3286,6 +3291,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
gettext_noop("permission denied for foreign-data wrapper %s"), gettext_noop("permission denied for foreign-data wrapper %s"),
/* ACL_KIND_FOREIGN_SERVER */ /* ACL_KIND_FOREIGN_SERVER */
gettext_noop("permission denied for foreign server %s"), gettext_noop("permission denied for foreign server %s"),
/* ACL_KIND_EVENT_TRIGGER */
gettext_noop("permission denied for event trigger %s"),
/* ACL_KIND_EXTENSION */ /* ACL_KIND_EXTENSION */
gettext_noop("permission denied for extension %s"), 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"), gettext_noop("must be owner of foreign-data wrapper %s"),
/* ACL_KIND_FOREIGN_SERVER */ /* ACL_KIND_FOREIGN_SERVER */
gettext_noop("must be owner of foreign server %s"), 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 */ /* ACL_KIND_EXTENSION */
gettext_noop("must be owner of extension %s"), 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); return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how);
case ACL_KIND_FOREIGN_SERVER: case ACL_KIND_FOREIGN_SERVER:
return pg_foreign_server_aclmask(table_oid, roleid, mask, how); 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: case ACL_KIND_TYPE:
return pg_type_aclmask(table_oid, roleid, mask, how); return pg_type_aclmask(table_oid, roleid, mask, how);
default: default:
@ -4875,6 +4888,33 @@ pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid)
return has_privs_of_role(roleid, ownerId); 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). * Ownership check for a database (specified by OID).
*/ */

View File

@ -34,6 +34,7 @@
#include "catalog/pg_database.h" #include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h" #include "catalog/pg_default_acl.h"
#include "catalog/pg_depend.h" #include "catalog/pg_depend.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_extension.h" #include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h" #include "catalog/pg_foreign_server.h"
@ -56,6 +57,7 @@
#include "commands/comment.h" #include "commands/comment.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/extension.h" #include "commands/extension.h"
#include "commands/proclang.h" #include "commands/proclang.h"
#include "commands/schemacmds.h" #include "commands/schemacmds.h"
@ -158,7 +160,8 @@ static const Oid object_classes[MAX_OCLASS] = {
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */ ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */ UserMappingRelationId, /* OCLASS_USER_MAPPING */
DefaultAclRelationId, /* OCLASS_DEFACL */ DefaultAclRelationId, /* OCLASS_DEFACL */
ExtensionRelationId /* OCLASS_EXTENSION */ ExtensionRelationId, /* OCLASS_EXTENSION */
EventTriggerRelationId /* OCLASS_EVENT_TRIGGER */
}; };
@ -1112,6 +1115,10 @@ doDeletion(const ObjectAddress *object, int flags)
break; break;
} }
case OCLASS_EVENT_TRIGGER:
RemoveEventTriggerById(object->objectId);
break;
case OCLASS_PROC: case OCLASS_PROC:
RemoveFunctionById(object->objectId); RemoveFunctionById(object->objectId);
break; break;
@ -2269,6 +2276,9 @@ getObjectClass(const ObjectAddress *object)
case ExtensionRelationId: case ExtensionRelationId:
return OCLASS_EXTENSION; return OCLASS_EXTENSION;
case EventTriggerRelationId:
return OCLASS_EVENT_TRIGGER;
} }
/* shouldn't get here */ /* shouldn't get here */
@ -2903,6 +2913,21 @@ getObjectDescription(const ObjectAddress *object)
break; 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: default:
appendStringInfo(&buffer, "unrecognized object %u %u %d", appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId, object->classId,

View File

@ -21,6 +21,7 @@
#include "catalog/objectaddress.h" #include "catalog/objectaddress.h"
#include "catalog/pg_authid.h" #include "catalog/pg_authid.h"
#include "catalog/pg_cast.h" #include "catalog/pg_cast.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_collation.h" #include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h" #include "catalog/pg_conversion.h"
@ -46,6 +47,7 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/extension.h" #include "commands/extension.h"
#include "commands/proclang.h" #include "commands/proclang.h"
#include "commands/tablespace.h" #include "commands/tablespace.h"
@ -203,6 +205,12 @@ static ObjectPropertyType ObjectProperty[] =
-1, -1,
InvalidAttrNumber InvalidAttrNumber
}, },
{
EventTriggerRelationId,
EventTriggerOidIndexId,
-1,
InvalidAttrNumber
},
{ {
TSConfigRelationId, TSConfigRelationId,
TSConfigOidIndexId, TSConfigOidIndexId,
@ -325,6 +333,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
case OBJECT_LANGUAGE: case OBJECT_LANGUAGE:
case OBJECT_FDW: case OBJECT_FDW:
case OBJECT_FOREIGN_SERVER: case OBJECT_FOREIGN_SERVER:
case OBJECT_EVENT_TRIGGER:
address = get_object_address_unqualified(objtype, address = get_object_address_unqualified(objtype,
objname, missing_ok); objname, missing_ok);
break; break;
@ -546,6 +555,9 @@ get_object_address_unqualified(ObjectType objtype,
case OBJECT_FOREIGN_SERVER: case OBJECT_FOREIGN_SERVER:
msg = gettext_noop("server name cannot be qualified"); msg = gettext_noop("server name cannot be qualified");
break; break;
case OBJECT_EVENT_TRIGGER:
msg = gettext_noop("event trigger name cannot be qualified");
break;
default: default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype); elog(ERROR, "unrecognized objtype: %d", (int) objtype);
msg = NULL; /* placate compiler */ msg = NULL; /* placate compiler */
@ -601,6 +613,11 @@ get_object_address_unqualified(ObjectType objtype,
address.objectId = get_foreign_server_oid(name, missing_ok); address.objectId = get_foreign_server_oid(name, missing_ok);
address.objectSubId = 0; address.objectSubId = 0;
break; break;
case OBJECT_EVENT_TRIGGER:
address.classId = EventTriggerRelationId;
address.objectId = get_event_trigger_oid(name, missing_ok);
address.objectSubId = 0;
break;
default: default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype); elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, which doesn't know elog won't return */ /* 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, aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
NameListToString(objname)); NameListToString(objname));
break; 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: case OBJECT_LANGUAGE:
if (!pg_language_ownercheck(address.objectId, roleid)) if (!pg_language_ownercheck(address.objectId, roleid))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE,

View File

@ -25,6 +25,7 @@
#include "catalog/pg_conversion.h" #include "catalog/pg_conversion.h"
#include "catalog/pg_database.h" #include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h" #include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_extension.h" #include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h" #include "catalog/pg_foreign_server.h"
@ -42,6 +43,7 @@
#include "commands/collationcmds.h" #include "commands/collationcmds.h"
#include "commands/conversioncmds.h" #include "commands/conversioncmds.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/extension.h" #include "commands/extension.h"
#include "commands/proclang.h" #include "commands/proclang.h"
#include "commands/schemacmds.h" #include "commands/schemacmds.h"
@ -1398,6 +1400,10 @@ shdepReassignOwned(List *roleids, Oid newrole)
AlterExtensionOwner_oid(sdepForm->objid, newrole); AlterExtensionOwner_oid(sdepForm->objid, newrole);
break; break;
case EventTriggerRelationId:
AlterEventTriggerOwner_oid(sdepForm->objid, newrole);
break;
default: default:
elog(ERROR, "unexpected classid %u", sdepForm->classid); elog(ERROR, "unexpected classid %u", sdepForm->classid);
break; break;

View File

@ -310,6 +310,19 @@ FROM
WHERE WHERE
l.objsubid = 0 l.objsubid = 0
UNION ALL 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 SELECT
l.objoid, l.classoid, 0::int4 AS objsubid, l.objoid, l.classoid, 0::int4 AS objsubid,
'database'::text AS objtype, 'database'::text AS objtype,

View File

@ -14,8 +14,8 @@ include $(top_builddir)/src/Makefile.global
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \ OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
collationcmds.o constraint.o conversioncmds.o copy.o createas.o \ collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
dbcommands.o define.o discard.o dropcmds.o explain.o extension.o \ dbcommands.o define.o discard.o dropcmds.o \
foreigncmds.o functioncmds.o \ event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \ indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
portalcmds.o prepare.o proclang.o \ portalcmds.o prepare.o proclang.o \
schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \ schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \

View File

@ -24,6 +24,7 @@
#include "commands/conversioncmds.h" #include "commands/conversioncmds.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/extension.h" #include "commands/extension.h"
#include "commands/proclang.h" #include "commands/proclang.h"
#include "commands/schemacmds.h" #include "commands/schemacmds.h"
@ -77,6 +78,10 @@ ExecRenameStmt(RenameStmt *stmt)
RenameForeignServer(stmt->subname, stmt->newname); RenameForeignServer(stmt->subname, stmt->newname);
break; break;
case OBJECT_EVENT_TRIGGER:
RenameEventTrigger(stmt->subname, stmt->newname);
break;
case OBJECT_FUNCTION: case OBJECT_FUNCTION:
RenameFunction(stmt->object, stmt->objarg, stmt->newname); RenameFunction(stmt->object, stmt->objarg, stmt->newname);
break; break;
@ -534,6 +539,10 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
AlterForeignServerOwner(strVal(linitial(stmt->object)), newowner); AlterForeignServerOwner(strVal(linitial(stmt->object)), newowner);
break; break;
case OBJECT_EVENT_TRIGGER:
AlterEventTriggerOwner(strVal(linitial(stmt->object)), newowner);
break;
default: default:
elog(ERROR, "unrecognized AlterOwnerStmt type: %d", elog(ERROR, "unrecognized AlterOwnerStmt type: %d",
(int) stmt->objectType); (int) stmt->objectType);

View File

@ -206,6 +206,10 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs)
args = NameListToString(list_truncate(objname, args = NameListToString(list_truncate(objname,
list_length(objname) - 1)); list_length(objname) - 1));
break; break;
case OBJECT_EVENT_TRIGGER:
msg = gettext_noop("event trigger \"%s\" does not exist, skipping");
name = NameListToString(objname);
break;
case OBJECT_RULE: case OBJECT_RULE:
msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping"); msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping");
name = strVal(llast(objname)); name = strVal(llast(objname));

View 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;
}

View File

@ -3466,6 +3466,30 @@ _copyCreateTrigStmt(const CreateTrigStmt *from)
return newnode; 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 * static CreatePLangStmt *
_copyCreatePLangStmt(const CreatePLangStmt *from) _copyCreatePLangStmt(const CreatePLangStmt *from)
{ {
@ -4317,6 +4341,12 @@ copyObject(const void *from)
case T_CreateTrigStmt: case T_CreateTrigStmt:
retval = _copyCreateTrigStmt(from); retval = _copyCreateTrigStmt(from);
break; break;
case T_CreateEventTrigStmt:
retval = _copyCreateEventTrigStmt(from);
break;
case T_AlterEventTrigStmt:
retval = _copyAlterEventTrigStmt(from);
break;
case T_CreatePLangStmt: case T_CreatePLangStmt:
retval = _copyCreatePLangStmt(from); retval = _copyCreatePLangStmt(from);
break; break;

View File

@ -1792,6 +1792,26 @@ _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
return true; 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 static bool
_equalCreatePLangStmt(const CreatePLangStmt *a, const CreatePLangStmt *b) _equalCreatePLangStmt(const CreatePLangStmt *a, const CreatePLangStmt *b)
{ {
@ -2872,6 +2892,12 @@ equal(const void *a, const void *b)
case T_CreateTrigStmt: case T_CreateTrigStmt:
retval = _equalCreateTrigStmt(a, b); retval = _equalCreateTrigStmt(a, b);
break; break;
case T_CreateEventTrigStmt:
retval = _equalCreateEventTrigStmt(a, b);
break;
case T_AlterEventTrigStmt:
retval = _equalAlterEventTrigStmt(a, b);
break;
case T_CreatePLangStmt: case T_CreatePLangStmt:
retval = _equalCreatePLangStmt(a, b); retval = _equalCreatePLangStmt(a, b);
break; break;

View File

@ -55,6 +55,7 @@
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_trigger.h" #include "catalog/pg_trigger.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/trigger.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
#include "parser/gramparse.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 %type <node> stmt schema_stmt
AlterEventTrigStmt
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
AlterFdwStmt AlterForeignServerStmt AlterGroupStmt AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
@ -207,7 +209,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertStmt CreateTrigStmt CreateAssertStmt CreateTrigStmt CreateEventTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreateUserStmt CreateUserMappingStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
@ -268,6 +270,10 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
%type <value> TriggerFuncArg %type <value> TriggerFuncArg
%type <node> TriggerWhen %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 %type <str> copy_file_name
database_name access_method_clause access_method attr_name database_name access_method_clause access_method attr_name
name cursor_name file_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 DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DESC
DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP 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 EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT EXTENSION EXTERNAL EXTRACT
@ -674,7 +680,8 @@ stmtmulti: stmtmulti ';' stmt
; ;
stmt : stmt :
AlterDatabaseStmt AlterEventTrigStmt
| AlterDatabaseStmt
| AlterDatabaseSetStmt | AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt | AlterDefaultPrivilegesStmt
| AlterDomainStmt | AlterDomainStmt
@ -725,6 +732,7 @@ stmt :
| CreateStmt | CreateStmt
| CreateTableSpaceStmt | CreateTableSpaceStmt
| CreateTrigStmt | CreateTrigStmt
| CreateEventTrigStmt
| CreateRoleStmt | CreateRoleStmt
| CreateUserStmt | CreateUserStmt
| CreateUserMappingStmt | CreateUserMappingStmt
@ -3554,6 +3562,15 @@ AlterExtensionContentsStmt:
n->objname = list_make1(makeString($6)); n->objname = list_make1(makeString($6));
$$ = (Node *)n; $$ = (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 | ALTER EXTENSION name add_drop TABLE any_name
{ {
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); 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 : * QUERIES :
@ -4868,6 +4954,7 @@ drop_type: TABLE { $$ = OBJECT_TABLE; }
| VIEW { $$ = OBJECT_VIEW; } | VIEW { $$ = OBJECT_VIEW; }
| INDEX { $$ = OBJECT_INDEX; } | INDEX { $$ = OBJECT_INDEX; }
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| TYPE_P { $$ = OBJECT_TYPE; } | TYPE_P { $$ = OBJECT_TYPE; }
| DOMAIN_P { $$ = OBJECT_DOMAIN; } | DOMAIN_P { $$ = OBJECT_DOMAIN; }
| COLLATION { $$ = OBJECT_COLLATION; } | COLLATION { $$ = OBJECT_COLLATION; }
@ -4931,7 +5018,7 @@ opt_restart_seqs:
* EXTENSION | ROLE | TEXT SEARCH PARSER | * EXTENSION | ROLE | TEXT SEARCH PARSER |
* TEXT SEARCH DICTIONARY | TEXT SEARCH TEMPLATE | * TEXT SEARCH DICTIONARY | TEXT SEARCH TEMPLATE |
* TEXT SEARCH CONFIGURATION | FOREIGN TABLE | * TEXT SEARCH CONFIGURATION | FOREIGN TABLE |
* FOREIGN DATA WRAPPER | SERVER ] <objname> | * FOREIGN DATA WRAPPER | SERVER | EVENT TRIGGER ] <objname> |
* AGGREGATE <aggname> (arg1, ...) | * AGGREGATE <aggname> (arg1, ...) |
* FUNCTION <funcname> (arg1, arg2, ...) | * FUNCTION <funcname> (arg1, arg2, ...) |
* OPERATOR <op> (leftoperand_typ, rightoperand_typ) | * OPERATOR <op> (leftoperand_typ, rightoperand_typ) |
@ -5113,6 +5200,7 @@ comment_type:
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
| SERVER { $$ = OBJECT_FOREIGN_SERVER; } | SERVER { $$ = OBJECT_FOREIGN_SERVER; }
| FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; } | FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
; ;
comment_text: comment_text:
@ -5195,6 +5283,7 @@ opt_provider: FOR ColId_or_Sconst { $$ = $2; }
security_label_type: security_label_type:
COLUMN { $$ = OBJECT_COLUMN; } COLUMN { $$ = OBJECT_COLUMN; }
| DATABASE { $$ = OBJECT_DATABASE; } | DATABASE { $$ = OBJECT_DATABASE; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
| SCHEMA { $$ = OBJECT_SCHEMA; } | SCHEMA { $$ = OBJECT_SCHEMA; }
| SEQUENCE { $$ = OBJECT_SEQUENCE; } | SEQUENCE { $$ = OBJECT_SEQUENCE; }
@ -6850,6 +6939,14 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
n->missing_ok = false; n->missing_ok = false;
$$ = (Node *)n; $$ = (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 | ALTER ROLE RoleId RENAME TO RoleId
{ {
RenameStmt *n = makeNode(RenameStmt); RenameStmt *n = makeNode(RenameStmt);
@ -7329,6 +7426,14 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId
n->newowner = $6; n->newowner = $6;
$$ = (Node *)n; $$ = (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;
}
; ;

View File

@ -33,6 +33,7 @@
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/discard.h" #include "commands/discard.h"
#include "commands/event_trigger.h"
#include "commands/explain.h" #include "commands/explain.h"
#include "commands/extension.h" #include "commands/extension.h"
#include "commands/lockcmds.h" #include "commands/lockcmds.h"
@ -183,6 +184,8 @@ check_xact_readonly(Node *parsetree)
case T_CommentStmt: case T_CommentStmt:
case T_DefineStmt: case T_DefineStmt:
case T_CreateCastStmt: case T_CreateCastStmt:
case T_CreateEventTrigStmt:
case T_AlterEventTrigStmt:
case T_CreateConversionStmt: case T_CreateConversionStmt:
case T_CreatedbStmt: case T_CreatedbStmt:
case T_CreateDomainStmt: case T_CreateDomainStmt:
@ -1056,6 +1059,14 @@ standard_ProcessUtility(Node *parsetree,
InvalidOid, InvalidOid, false); InvalidOid, InvalidOid, false);
break; break;
case T_CreateEventTrigStmt:
CreateEventTrigger((CreateEventTrigStmt *) parsetree);
break;
case T_AlterEventTrigStmt:
AlterEventTrigger((AlterEventTrigStmt *) parsetree);
break;
case T_CreatePLangStmt: case T_CreatePLangStmt:
CreateProceduralLanguage((CreatePLangStmt *) parsetree); CreateProceduralLanguage((CreatePLangStmt *) parsetree);
break; break;
@ -1472,6 +1483,9 @@ AlterObjectTypeCommandTag(ObjectType objtype)
case OBJECT_TRIGGER: case OBJECT_TRIGGER:
tag = "ALTER TRIGGER"; tag = "ALTER TRIGGER";
break; break;
case OBJECT_EVENT_TRIGGER:
tag = "ALTER EVENT TRIGGER";
break;
case OBJECT_TSCONFIGURATION: case OBJECT_TSCONFIGURATION:
tag = "ALTER TEXT SEARCH CONFIGURATION"; tag = "ALTER TEXT SEARCH CONFIGURATION";
break; break;
@ -1741,6 +1755,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_TRIGGER: case OBJECT_TRIGGER:
tag = "DROP TRIGGER"; tag = "DROP TRIGGER";
break; break;
case OBJECT_EVENT_TRIGGER:
tag = "DROP EVENT TRIGGER";
break;
case OBJECT_RULE: case OBJECT_RULE:
tag = "DROP RULE"; tag = "DROP RULE";
break; break;
@ -1994,6 +2011,14 @@ CreateCommandTag(Node *parsetree)
tag = "CREATE TRIGGER"; tag = "CREATE TRIGGER";
break; break;
case T_CreateEventTrigStmt:
tag = "CREATE EVENT TRIGGER";
break;
case T_AlterEventTrigStmt:
tag = "ALTER EVENT TRIGGER";
break;
case T_CreatePLangStmt: case T_CreatePLangStmt:
tag = "CREATE LANGUAGE"; tag = "CREATE LANGUAGE";
break; break;
@ -2489,6 +2514,14 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL; lev = LOGSTMT_DDL;
break; break;
case T_CreateEventTrigStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterEventTrigStmt:
lev = LOGSTMT_DDL;
break;
case T_CreatePLangStmt: case T_CreatePLangStmt:
lev = LOGSTMT_DDL; lev = LOGSTMT_DDL;
break; break;

View File

@ -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. * language_handler_in - input routine for pseudo-type LANGUAGE_HANDLER.
*/ */

View File

@ -34,6 +34,7 @@
#include "catalog/pg_database.h" #include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h" #include "catalog/pg_default_acl.h"
#include "catalog/pg_enum.h" #include "catalog/pg_enum.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h" #include "catalog/pg_foreign_server.h"
#include "catalog/pg_foreign_table.h" #include "catalog/pg_foreign_table.h"
@ -379,6 +380,28 @@ static const struct cachedesc cacheinfo[] = {
}, },
256 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 */ {ForeignDataWrapperRelationId, /* FOREIGNDATAWRAPPERNAME */
ForeignDataWrapperNameIndexId, ForeignDataWrapperNameIndexId,
1, 1,

View File

@ -100,6 +100,7 @@ getSchemaData(Archive *fout, int *numTablesPtr)
int numForeignDataWrappers; int numForeignDataWrappers;
int numForeignServers; int numForeignServers;
int numDefaultACLs; int numDefaultACLs;
int numEventTriggers;
if (g_verbose) if (g_verbose)
write_msg(NULL, "reading schemas\n"); write_msg(NULL, "reading schemas\n");
@ -240,6 +241,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading triggers\n"); write_msg(NULL, "reading triggers\n");
getTriggers(fout, tblinfo, numTables); getTriggers(fout, tblinfo, numTables);
if (g_verbose)
write_msg(NULL, "reading event triggers\n");
getEventTriggers(fout, &numEventTriggers);
*numTablesPtr = numTables; *numTablesPtr = numTables;
return tblinfo; return tblinfo;
} }

View File

@ -49,6 +49,7 @@
#include "catalog/pg_cast.h" #include "catalog/pg_cast.h"
#include "catalog/pg_class.h" #include "catalog/pg_class.h"
#include "catalog/pg_default_acl.h" #include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_largeobject.h" #include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h" #include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_proc.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 dumpRule(Archive *fout, RuleInfo *rinfo);
static void dumpAgg(Archive *fout, AggInfo *agginfo); static void dumpAgg(Archive *fout, AggInfo *agginfo);
static void dumpTrigger(Archive *fout, TriggerInfo *tginfo); static void dumpTrigger(Archive *fout, TriggerInfo *tginfo);
static void dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo);
static void dumpTable(Archive *fout, TableInfo *tbinfo); static void dumpTable(Archive *fout, TableInfo *tbinfo);
static void dumpTableSchema(Archive *fout, TableInfo *tbinfo); static void dumpTableSchema(Archive *fout, TableInfo *tbinfo);
static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo); static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo);
@ -5296,6 +5298,87 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
destroyPQExpBuffer(query); 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 * getProcLangs
* get basic information about every procedural language in the system * get basic information about every procedural language in the system
@ -7166,6 +7249,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_TRIGGER: case DO_TRIGGER:
dumpTrigger(fout, (TriggerInfo *) dobj); dumpTrigger(fout, (TriggerInfo *) dobj);
break; break;
case DO_EVENT_TRIGGER:
dumpEventTrigger(fout, (EventTriggerInfo *) dobj);
break;
case DO_CONSTRAINT: case DO_CONSTRAINT:
dumpConstraint(fout, (ConstraintInfo *) dobj); dumpConstraint(fout, (ConstraintInfo *) dobj);
break; break;
@ -13658,6 +13744,69 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
destroyPQExpBuffer(labelq); 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 * dumpRule
* Dump a rule * Dump a rule
@ -14153,6 +14302,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
break; break;
case DO_INDEX: case DO_INDEX:
case DO_TRIGGER: case DO_TRIGGER:
case DO_EVENT_TRIGGER:
case DO_DEFAULT_ACL: case DO_DEFAULT_ACL:
/* Post-data objects: must come after the post-data boundary */ /* Post-data objects: must come after the post-data boundary */
addObjectDependency(dobj, postDataBound->dumpId); addObjectDependency(dobj, postDataBound->dumpId);

View File

@ -120,7 +120,8 @@ typedef enum
DO_BLOB, DO_BLOB,
DO_BLOB_DATA, DO_BLOB_DATA,
DO_PRE_DATA_BOUNDARY, DO_PRE_DATA_BOUNDARY,
DO_POST_DATA_BOUNDARY DO_POST_DATA_BOUNDARY,
DO_EVENT_TRIGGER
} DumpableObjectType; } DumpableObjectType;
typedef struct _dumpableObject typedef struct _dumpableObject
@ -352,6 +353,18 @@ typedef struct _triggerInfo
char *tgdef; char *tgdef;
} TriggerInfo; } 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 * struct ConstraintInfo is used for all constraint types. However we
* use a different objType for foreign key constraints, to make it easier * 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 DefaultACLInfo *getDefaultACLs(Archive *fout, int *numDefaultACLs);
extern void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], extern void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
int numExtensions); int numExtensions);
extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
#endif /* PG_DUMP_H */ #endif /* PG_DUMP_H */

View File

@ -24,8 +24,9 @@ static const char *modulename = gettext_noop("sorter");
* Objects are sorted by priority levels, and within an equal priority level * Objects are sorted by priority levels, and within an equal priority level
* by OID. (This is a relatively crude hack to provide semi-reasonable * by OID. (This is a relatively crude hack to provide semi-reasonable
* behavior for old databases without full dependency info.) Note: collations, * behavior for old databases without full dependency info.) Note: collations,
* extensions, text search, foreign-data, and default ACL objects can't really * extensions, text search, foreign-data, event trigger, and default ACL
* happen here, so the rather bogus priorities for them don't matter. * 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 * 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, * 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 */ 9, /* DO_BLOB */
12, /* DO_BLOB_DATA */ 12, /* DO_BLOB_DATA */
10, /* DO_PRE_DATA_BOUNDARY */ 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 */ 21, /* DO_BLOB */
24, /* DO_BLOB_DATA */ 24, /* DO_BLOB_DATA */
22, /* DO_PRE_DATA_BOUNDARY */ 22, /* DO_PRE_DATA_BOUNDARY */
25 /* DO_POST_DATA_BOUNDARY */ 25, /* DO_POST_DATA_BOUNDARY */
32 /* DO_EVENT_TRIGGER */
}; };
static DumpId preDataBoundId; static DumpId preDataBoundId;
@ -1147,6 +1150,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"TRIGGER %s (ID %d OID %u)", "TRIGGER %s (ID %d OID %u)",
obj->name, obj->dumpId, obj->catId.oid); obj->name, obj->dumpId, obj->catId.oid);
return; 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: case DO_CONSTRAINT:
snprintf(buf, bufsize, snprintf(buf, bufsize,
"CONSTRAINT %s (ID %d OID %u)", "CONSTRAINT %s (ID %d OID %u)",

View File

@ -490,6 +490,9 @@ exec_command(const char *cmd,
else else
success = listExtensions(pattern); success = listExtensions(pattern);
break; break;
case 'y': /* Event Triggers */
success = listEventTriggers(pattern, show_verbose);
break;
default: default:
status = PSQL_CMD_UNKNOWN; status = PSQL_CMD_UNKNOWN;
} }

View File

@ -2952,6 +2952,67 @@ listConversions(const char *pattern, bool verbose, bool showSystem)
return true; 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 * \dC
* *

View File

@ -96,4 +96,7 @@ extern bool listExtensions(const char *pattern);
/* \dx+ */ /* \dx+ */
extern bool listExtensionContents(const char *pattern); extern bool listExtensionContents(const char *pattern);
/* \dy */
extern bool listEventTriggers(const char *pattern, bool verbose);
#endif /* DESCRIBE_H */ #endif /* DESCRIBE_H */

View File

@ -229,6 +229,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n")); fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n")); fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\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, _(" \\l[+] list all databases\n"));
fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n")); fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n"));
fprintf(output, _(" \\z [PATTERN] same as \\dp\n")); fprintf(output, _(" \\z [PATTERN] same as \\dp\n"));

View File

@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201207111 #define CATALOG_VERSION_NO 201207181
#endif #endif

View File

@ -146,6 +146,7 @@ typedef enum ObjectClass
OCLASS_USER_MAPPING, /* pg_user_mapping */ OCLASS_USER_MAPPING, /* pg_user_mapping */
OCLASS_DEFACL, /* pg_default_acl */ OCLASS_DEFACL, /* pg_default_acl */
OCLASS_EXTENSION, /* pg_extension */ OCLASS_EXTENSION, /* pg_extension */
OCLASS_EVENT_TRIGGER, /* pg_event_trigger */
MAX_OCLASS /* MUST BE LAST */ MAX_OCLASS /* MUST BE LAST */
} ObjectClass; } ObjectClass;

View File

@ -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)); DECLARE_UNIQUE_INDEX(pg_trigger_oid_index, 2702, on pg_trigger using btree(oid oid_ops));
#define TriggerOidIndexId 2702 #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)); DECLARE_UNIQUE_INDEX(pg_ts_config_cfgname_index, 3608, on pg_ts_config using btree(cfgname name_ops, cfgnamespace oid_ops));
#define TSConfigNameNspIndexId 3608 #define TSConfigNameNspIndexId 3608
DECLARE_UNIQUE_INDEX(pg_ts_config_oid_index, 3712, on pg_ts_config using btree(oid oid_ops)); DECLARE_UNIQUE_INDEX(pg_ts_config_oid_index, 3712, on pg_ts_config using btree(oid oid_ops));

View 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 */

View File

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

View File

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

View 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 */

View File

@ -357,6 +357,8 @@ typedef enum NodeTag
T_CreateExtensionStmt, T_CreateExtensionStmt,
T_AlterExtensionStmt, T_AlterExtensionStmt,
T_AlterExtensionContentsStmt, T_AlterExtensionContentsStmt,
T_CreateEventTrigStmt,
T_AlterEventTrigStmt,
/* /*
* TAGS FOR PARSE TREE NODES (parsenodes.h) * TAGS FOR PARSE TREE NODES (parsenodes.h)

View File

@ -1113,6 +1113,7 @@ typedef enum ObjectType
OBJECT_CONVERSION, OBJECT_CONVERSION,
OBJECT_DATABASE, OBJECT_DATABASE,
OBJECT_DOMAIN, OBJECT_DOMAIN,
OBJECT_EVENT_TRIGGER,
OBJECT_EXTENSION, OBJECT_EXTENSION,
OBJECT_FDW, OBJECT_FDW,
OBJECT_FOREIGN_SERVER, OBJECT_FOREIGN_SERVER,
@ -1731,6 +1732,32 @@ typedef struct CreateTrigStmt
} 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 * Create PROCEDURAL LANGUAGE Statements
* ---------------------- * ----------------------
*/ */

View File

@ -141,6 +141,7 @@ PG_KEYWORD("encrypted", ENCRYPTED, UNRESERVED_KEYWORD)
PG_KEYWORD("end", END_P, RESERVED_KEYWORD) PG_KEYWORD("end", END_P, RESERVED_KEYWORD)
PG_KEYWORD("enum", ENUM_P, UNRESERVED_KEYWORD) PG_KEYWORD("enum", ENUM_P, UNRESERVED_KEYWORD)
PG_KEYWORD("escape", ESCAPE, UNRESERVED_KEYWORD) PG_KEYWORD("escape", ESCAPE, UNRESERVED_KEYWORD)
PG_KEYWORD("event", EVENT, UNRESERVED_KEYWORD)
PG_KEYWORD("except", EXCEPT, RESERVED_KEYWORD) PG_KEYWORD("except", EXCEPT, RESERVED_KEYWORD)
PG_KEYWORD("exclude", EXCLUDE, UNRESERVED_KEYWORD) PG_KEYWORD("exclude", EXCLUDE, UNRESERVED_KEYWORD)
PG_KEYWORD("excluding", EXCLUDING, UNRESERVED_KEYWORD) PG_KEYWORD("excluding", EXCLUDING, UNRESERVED_KEYWORD)

View File

@ -195,6 +195,7 @@ typedef enum AclObjectKind
ACL_KIND_TSCONFIGURATION, /* pg_ts_config */ ACL_KIND_TSCONFIGURATION, /* pg_ts_config */
ACL_KIND_FDW, /* pg_foreign_data_wrapper */ ACL_KIND_FDW, /* pg_foreign_data_wrapper */
ACL_KIND_FOREIGN_SERVER, /* pg_foreign_server */ ACL_KIND_FOREIGN_SERVER, /* pg_foreign_server */
ACL_KIND_EVENT_TRIGGER, /* pg_event_trigger */
ACL_KIND_EXTENSION, /* pg_extension */ ACL_KIND_EXTENSION, /* pg_extension */
MAX_ACL_KIND /* MUST BE LAST */ MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind; } 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_ts_config_ownercheck(Oid cfg_oid, Oid roleid);
extern bool pg_foreign_data_wrapper_ownercheck(Oid srv_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_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 pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid); extern bool has_createrole_privilege(Oid roleid);

View File

@ -532,6 +532,8 @@ extern Datum void_recv(PG_FUNCTION_ARGS);
extern Datum void_send(PG_FUNCTION_ARGS); extern Datum void_send(PG_FUNCTION_ARGS);
extern Datum trigger_in(PG_FUNCTION_ARGS); extern Datum trigger_in(PG_FUNCTION_ARGS);
extern Datum trigger_out(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_in(PG_FUNCTION_ARGS);
extern Datum language_handler_out(PG_FUNCTION_ARGS); extern Datum language_handler_out(PG_FUNCTION_ARGS);
extern Datum fdw_handler_in(PG_FUNCTION_ARGS); extern Datum fdw_handler_in(PG_FUNCTION_ARGS);

View File

@ -54,6 +54,8 @@ enum SysCacheIdentifier
DEFACLROLENSPOBJ, DEFACLROLENSPOBJ,
ENUMOID, ENUMOID,
ENUMTYPOIDNAME, ENUMTYPOIDNAME,
EVENTTRIGGERNAME,
EVENTTRIGGEROID,
FOREIGNDATAWRAPPERNAME, FOREIGNDATAWRAPPERNAME,
FOREIGNDATAWRAPPEROID, FOREIGNDATAWRAPPEROID,
FOREIGNSERVERNAME, FOREIGNSERVERNAME,

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

View File

@ -1276,8 +1276,8 @@ drop table cchild;
-- Check that ruleutils are working -- Check that ruleutils are working
-- --
SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname; 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); 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_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))); 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_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_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_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_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_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)); 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));

View File

@ -102,6 +102,7 @@ SELECT relname, relhasindex
pg_depend | t pg_depend | t
pg_description | t pg_description | t
pg_enum | t pg_enum | t
pg_event_trigger | t
pg_extension | t pg_extension | t
pg_foreign_data_wrapper | t pg_foreign_data_wrapper | t
pg_foreign_server | t pg_foreign_server | t
@ -164,7 +165,7 @@ SELECT relname, relhasindex
timetz_tbl | f timetz_tbl | f
tinterval_tbl | f tinterval_tbl | f
varchar_tbl | f varchar_tbl | f
(153 rows) (154 rows)
-- --
-- another sanity check: every system catalog that has OIDs should have -- another sanity check: every system catalog that has OIDs should have

View File

@ -88,6 +88,8 @@ test: privileges security_label collate
test: misc test: misc
# rules cannot run concurrently with any test that creates a view # rules cannot run concurrently with any test that creates a view
test: rules test: rules
# event triggers cannot run concurrently with any test that runs DDL
test: event_trigger
# ---------- # ----------
# Another group of parallel tests # Another group of parallel tests

View File

@ -96,6 +96,7 @@ test: security_label
test: collate test: collate
test: misc test: misc
test: rules test: rules
test: event_trigger
test: select_views test: select_views
test: portals_p2 test: portals_p2
test: foreign_key test: foreign_key

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