]> granicus.if.org Git - postgresql/commitdiff
Syntax support and documentation for event triggers.
authorRobert Haas <rhaas@postgresql.org>
Wed, 18 Jul 2012 14:16:16 +0000 (10:16 -0400)
committerRobert Haas <rhaas@postgresql.org>
Wed, 18 Jul 2012 14:16:16 +0000 (10:16 -0400)
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.

56 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/event-trigger.sgml [new file with mode: 0644]
doc/src/sgml/filelist.sgml
doc/src/sgml/postgres.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/alter_event_trigger.sgml [new file with mode: 0644]
doc/src/sgml/ref/alter_extension.sgml
doc/src/sgml/ref/comment.sgml
doc/src/sgml/ref/create_event_trigger.sgml [new file with mode: 0644]
doc/src/sgml/ref/drop_event_trigger.sgml [new file with mode: 0644]
doc/src/sgml/ref/psql-ref.sgml
doc/src/sgml/ref/security_label.sgml
doc/src/sgml/reference.sgml
src/backend/catalog/Makefile
src/backend/catalog/aclchk.c
src/backend/catalog/dependency.c
src/backend/catalog/objectaddress.c
src/backend/catalog/pg_shdepend.c
src/backend/catalog/system_views.sql
src/backend/commands/Makefile
src/backend/commands/alter.c
src/backend/commands/dropcmds.c
src/backend/commands/event_trigger.c [new file with mode: 0644]
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/tcop/utility.c
src/backend/utils/adt/pseudotypes.c
src/backend/utils/cache/syscache.c
src/bin/pg_dump/common.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/pg_dump/pg_dump_sort.c
src/bin/psql/command.c
src/bin/psql/describe.c
src/bin/psql/describe.h
src/bin/psql/help.c
src/include/catalog/catversion.h
src/include/catalog/dependency.h
src/include/catalog/indexing.h
src/include/catalog/pg_event_trigger.h [new file with mode: 0644]
src/include/catalog/pg_proc.h
src/include/catalog/pg_type.h
src/include/commands/event_trigger.h [new file with mode: 0644]
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/parser/kwlist.h
src/include/utils/acl.h
src/include/utils/builtins.h
src/include/utils/syscache.h
src/test/regress/expected/event_trigger.out [new file with mode: 0644]
src/test/regress/expected/rules.out
src/test/regress/expected/sanity_check.out
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/event_trigger.sql [new file with mode: 0644]

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