1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-30 11:03:19 +03:00

Offer triggers on foreign tables.

This covers all the SQL-standard trigger types supported for regular
tables; it does not cover constraint triggers.  The approach for
acquiring the old row mirrors that for view INSTEAD OF triggers.  For
AFTER ROW triggers, we spool the foreign tuples to a tuplestore.

This changes the FDW API contract; when deciding which columns to
populate in the slot returned from data modification callbacks, writable
FDWs will need to check for AFTER ROW triggers in addition to checking
for a RETURNING clause.

In support of the feature addition, refactor the TriggerFlags bits and
the assembly of old tuples in ModifyTable.

Ronan Dunklau, reviewed by KaiGai Kohei; some additional hacking by me.
This commit is contained in:
Noah Misch
2014-03-23 02:16:34 -04:00
parent 6115480c54
commit 7cbe57c34d
14 changed files with 1145 additions and 202 deletions

View File

@ -308,7 +308,8 @@ AddForeignUpdateTargets (Query *parsetree,
extra values to be fetched. Each such entry must be marked
<structfield>resjunk</> = <literal>true</>, and must have a distinct
<structfield>resname</> that will identify it at execution time.
Avoid using names matching <literal>ctid<replaceable>N</></literal> or
Avoid using names matching <literal>ctid<replaceable>N</></literal>,
<literal>wholerow</literal>, or
<literal>wholerow<replaceable>N</></literal>, as the core system can
generate junk columns of these names.
</para>
@ -447,11 +448,12 @@ ExecForeignInsert (EState *estate,
<para>
The data in the returned slot is used only if the <command>INSERT</>
query has a <literal>RETURNING</> clause. Hence, the FDW could choose
to optimize away returning some or all columns depending on the contents
of the <literal>RETURNING</> clause. However, some slot must be
returned to indicate success, or the query's reported row count will be
wrong.
query has a <literal>RETURNING</> clause or the foreign table has
an <literal>AFTER ROW</> trigger. Triggers require all columns, but the
FDW could choose to optimize away returning some or all columns depending
on the contents of the <literal>RETURNING</> clause. Regardless, some
slot must be returned to indicate success, or the query's reported row
count will be wrong.
</para>
<para>
@ -492,11 +494,12 @@ ExecForeignUpdate (EState *estate,
<para>
The data in the returned slot is used only if the <command>UPDATE</>
query has a <literal>RETURNING</> clause. Hence, the FDW could choose
to optimize away returning some or all columns depending on the contents
of the <literal>RETURNING</> clause. However, some slot must be
returned to indicate success, or the query's reported row count will be
wrong.
query has a <literal>RETURNING</> clause or the foreign table has
an <literal>AFTER ROW</> trigger. Triggers require all columns, but the
FDW could choose to optimize away returning some or all columns depending
on the contents of the <literal>RETURNING</> clause. Regardless, some
slot must be returned to indicate success, or the query's reported row
count will be wrong.
</para>
<para>
@ -535,11 +538,12 @@ ExecForeignDelete (EState *estate,
<para>
The data in the returned slot is used only if the <command>DELETE</>
query has a <literal>RETURNING</> clause. Hence, the FDW could choose
to optimize away returning some or all columns depending on the contents
of the <literal>RETURNING</> clause. However, some slot must be
returned to indicate success, or the query's reported row count will be
wrong.
query has a <literal>RETURNING</> clause or the foreign table has
an <literal>AFTER ROW</> trigger. Triggers require all columns, but the
FDW could choose to optimize away returning some or all columns depending
on the contents of the <literal>RETURNING</> clause. Regardless, some
slot must be returned to indicate success, or the query's reported row
count will be wrong.
</para>
<para>

View File

@ -43,9 +43,10 @@ CREATE [ CONSTRAINT ] TRIGGER <replaceable class="PARAMETER">name</replaceable>
<para>
<command>CREATE TRIGGER</command> creates a new trigger. The
trigger will be associated with the specified table or view and will
execute the specified function <replaceable
class="parameter">function_name</replaceable> when certain events occur.
trigger will be associated with the specified table, view, or foreign table
and will execute the specified
function <replaceable class="parameter">function_name</replaceable> when
certain events occur.
</para>
<para>
@ -93,7 +94,7 @@ CREATE [ CONSTRAINT ] TRIGGER <replaceable class="PARAMETER">name</replaceable>
<para>
The following table summarizes which types of triggers may be used on
tables and views:
tables, views, and foreign tables:
</para>
<informaltable id="supported-trigger-types">
@ -110,8 +111,8 @@ CREATE [ CONSTRAINT ] TRIGGER <replaceable class="PARAMETER">name</replaceable>
<row>
<entry align="center" morerows="1"><literal>BEFORE</></entry>
<entry align="center"><command>INSERT</>/<command>UPDATE</>/<command>DELETE</></entry>
<entry align="center">Tables</entry>
<entry align="center">Tables and views</entry>
<entry align="center">Tables and foreign tables</entry>
<entry align="center">Tables, views, and foreign tables</entry>
</row>
<row>
<entry align="center"><command>TRUNCATE</></entry>
@ -121,8 +122,8 @@ CREATE [ CONSTRAINT ] TRIGGER <replaceable class="PARAMETER">name</replaceable>
<row>
<entry align="center" morerows="1"><literal>AFTER</></entry>
<entry align="center"><command>INSERT</>/<command>UPDATE</>/<command>DELETE</></entry>
<entry align="center">Tables</entry>
<entry align="center">Tables and views</entry>
<entry align="center">Tables and foreign tables</entry>
<entry align="center">Tables, views, and foreign tables</entry>
</row>
<row>
<entry align="center"><command>TRUNCATE</></entry>
@ -164,13 +165,13 @@ CREATE [ CONSTRAINT ] TRIGGER <replaceable class="PARAMETER">name</replaceable>
<firstterm>constraint trigger</>. This is the same as a regular trigger
except that the timing of the trigger firing can be adjusted using
<xref linkend="SQL-SET-CONSTRAINTS">.
Constraint triggers must be <literal>AFTER ROW</> triggers. They can
be fired either at the end of the statement causing the triggering event,
or at the end of the containing transaction; in the latter case they are
said to be <firstterm>deferred</>. A pending deferred-trigger firing can
also be forced to happen immediately by using <command>SET CONSTRAINTS</>.
Constraint triggers are expected to raise an exception when the constraints
they implement are violated.
Constraint triggers must be <literal>AFTER ROW</> triggers on tables. They
can be fired either at the end of the statement causing the triggering
event, or at the end of the containing transaction; in the latter case they
are said to be <firstterm>deferred</>. A pending deferred-trigger firing
can also be forced to happen immediately by using <command>SET
CONSTRAINTS</>. Constraint triggers are expected to raise an exception
when the constraints they implement are violated.
</para>
<para>
@ -244,8 +245,8 @@ UPDATE OF <replaceable>column_name1</replaceable> [, <replaceable>column_name2</
<term><replaceable class="parameter">table_name</replaceable></term>
<listitem>
<para>
The name (optionally schema-qualified) of the table or view the trigger
is for.
The name (optionally schema-qualified) of the table, view, or foreign
table the trigger is for.
</para>
</listitem>
</varlistentry>
@ -481,6 +482,14 @@ CREATE TRIGGER view_insert
<refsect1 id="SQL-CREATETRIGGER-compatibility">
<title>Compatibility</title>
<!--
It's not clear whether SQL/MED contemplates triggers on foreign tables.
Its <drop basic column definition> General Rules do mention the possibility
of a reference from a trigger column list. On the other hand, nothing
overrides the fact that CREATE TRIGGER only targets base tables. For now,
do not document the compatibility status of triggers on foreign tables.
-->
<para>
The <command>CREATE TRIGGER</command> statement in
<productname>PostgreSQL</productname> implements a subset of the

View File

@ -33,20 +33,21 @@
<para>
A trigger is a specification that the database should automatically
execute a particular function whenever a certain type of operation is
performed. Triggers can be attached to both tables and views.
performed. Triggers can be attached to tables, views, and foreign tables.
</para>
<para>
On tables, triggers can be defined to execute either before or after any
<command>INSERT</command>, <command>UPDATE</command>, or
<command>DELETE</command> operation, either once per modified row,
On tables and foreign tables, triggers can be defined to execute either
before or after any <command>INSERT</command>, <command>UPDATE</command>,
or <command>DELETE</command> operation, either once per modified row,
or once per <acronym>SQL</acronym> statement.
<command>UPDATE</command> triggers can moreover be set to fire only if
certain columns are mentioned in the <literal>SET</literal> clause of the
<command>UPDATE</command> statement.
Triggers can also fire for <command>TRUNCATE</command> statements.
If a trigger event occurs, the trigger's function is called at the
appropriate time to handle the event.
appropriate time to handle the event. Foreign tables do not support the
TRUNCATE statement at all.
</para>
<para>
@ -111,10 +112,10 @@
triggers fire immediately before a particular row is operated on,
while row-level <literal>AFTER</> triggers fire at the end of the
statement (but before any statement-level <literal>AFTER</> triggers).
These types of triggers may only be defined on tables. Row-level
<literal>INSTEAD OF</> triggers may only be defined on views, and fire
immediately as each row in the view is identified as needing to be
operated on.
These types of triggers may only be defined on tables and foreign tables.
Row-level <literal>INSTEAD OF</> triggers may only be defined on views,
and fire immediately as each row in the view is identified as needing to
be operated on.
</para>
<para>
@ -548,7 +549,8 @@ typedef struct TriggerData
<command>DELETE</command> then this is what you should return
from the function if you don't want to replace the row with
a different one (in the case of <command>INSERT</command>) or
skip the operation.
skip the operation. For triggers on foreign tables, values of system
columns herein are unspecified.
</para>
</listitem>
</varlistentry>
@ -563,7 +565,8 @@ typedef struct TriggerData
<command>DELETE</command>. This is what you have to return
from the function if the event is an <command>UPDATE</command>
and you don't want to replace this row by a different one or
skip the operation.
skip the operation. For triggers on foreign tables, values of system
columns herein are unspecified.
</para>
</listitem>
</varlistentry>