Commit 7692d8d5 authored by Tom Lane's avatar Tom Lane

Support statement-level ON TRUNCATE triggers. Simon Riggs

parent 107b3d0c
<!-- $PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.67 2008/01/25 15:28:35 adunstan Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.68 2008/03/28 00:21:55 tgl Exp $ -->
<chapter id="plperl"> <chapter id="plperl">
<title>PL/Perl - Perl Procedural Language</title> <title>PL/Perl - Perl Procedural Language</title>
...@@ -17,12 +17,14 @@ ...@@ -17,12 +17,14 @@
<ulink url="http://www.perl.com">Perl programming language</ulink>. <ulink url="http://www.perl.com">Perl programming language</ulink>.
</para> </para>
<para> The usual advantage to using PL/Perl is that this allows use, <para>
The main advantage to using PL/Perl is that this allows use,
within stored functions, of the manyfold <quote>string within stored functions, of the manyfold <quote>string
munging</quote> operators and functions available for Perl. Parsing munging</quote> operators and functions available for Perl. Parsing
complex strings might be easier using Perl than it is with the complex strings might be easier using Perl than it is with the
string functions and control structures provided in PL/pgSQL.</para> string functions and control structures provided in PL/pgSQL.
</para>
<para> <para>
To install PL/Perl in a particular database, use To install PL/Perl in a particular database, use
<literal>createlang plperl <replaceable>dbname</></literal>. <literal>createlang plperl <replaceable>dbname</></literal>.
...@@ -739,7 +741,8 @@ $$ LANGUAGE plperl; ...@@ -739,7 +741,8 @@ $$ LANGUAGE plperl;
<term><literal>$_TD-&gt;{event}</literal></term> <term><literal>$_TD-&gt;{event}</literal></term>
<listitem> <listitem>
<para> <para>
Trigger event: <literal>INSERT</>, <literal>UPDATE</>, <literal>DELETE</>, or <literal>UNKNOWN</> Trigger event: <literal>INSERT</>, <literal>UPDATE</>,
<literal>DELETE</>, <literal>TRUNCATE</>, or <literal>UNKNOWN</>
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -822,14 +825,14 @@ $$ LANGUAGE plperl; ...@@ -822,14 +825,14 @@ $$ LANGUAGE plperl;
</para> </para>
<para> <para>
Triggers can return one of the following: Row-level triggers can return one of the following:
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><literal>return;</literal></term> <term><literal>return;</literal></term>
<listitem> <listitem>
<para> <para>
Execute the statement Execute the operation
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -838,7 +841,7 @@ $$ LANGUAGE plperl; ...@@ -838,7 +841,7 @@ $$ LANGUAGE plperl;
<term><literal>"SKIP"</literal></term> <term><literal>"SKIP"</literal></term>
<listitem> <listitem>
<para> <para>
Don't execute the statement Don't execute the operation
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.124 2008/03/23 00:24:19 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.125 2008/03/28 00:21:55 tgl Exp $ -->
<chapter id="plpgsql"> <chapter id="plpgsql">
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title> <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
...@@ -2785,9 +2785,9 @@ RAISE EXCEPTION 'Nonexistent ID --> %', user_id; ...@@ -2785,9 +2785,9 @@ RAISE EXCEPTION 'Nonexistent ID --> %', user_id;
<listitem> <listitem>
<para> <para>
Data type <type>text</type>; a string of Data type <type>text</type>; a string of
<literal>INSERT</literal>, <literal>UPDATE</literal>, or <literal>INSERT</literal>, <literal>UPDATE</literal>,
<literal>DELETE</literal> telling for which operation the <literal>DELETE</literal>, or <literal>TRUNCATE</>
trigger was fired. telling for which operation the trigger was fired.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpython.sgml,v 1.38 2007/02/01 00:28:17 momjian Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/plpython.sgml,v 1.39 2008/03/28 00:21:55 tgl Exp $ -->
<chapter id="plpython"> <chapter id="plpython">
<title>PL/Python - Python Procedural Language</title> <title>PL/Python - Python Procedural Language</title>
...@@ -381,31 +381,34 @@ $$ LANGUAGE plpythonu; ...@@ -381,31 +381,34 @@ $$ LANGUAGE plpythonu;
<para> <para>
When a function is used as a trigger, the dictionary When a function is used as a trigger, the dictionary
<literal>TD</literal> contains trigger-related values. The trigger <literal>TD</literal> contains trigger-related values.
rows are in <literal>TD["new"]</> and/or <literal>TD["old"]</> <literal>TD["event"]</> contains
depending on the trigger event. <literal>TD["event"]</> contains
the event as a string (<literal>INSERT</>, <literal>UPDATE</>, the event as a string (<literal>INSERT</>, <literal>UPDATE</>,
<literal>DELETE</>, or <literal>UNKNOWN</>). <literal>DELETE</>, <literal>TRUNCATE</>, or <literal>UNKNOWN</>).
<literal>TD["when"]</> contains one of <literal>BEFORE</>, <literal>TD["when"]</> contains one of <literal>BEFORE</>,
<literal>AFTER</>, and <literal>UNKNOWN</>. <literal>AFTER</>, or <literal>UNKNOWN</>.
<literal>TD["level"]</> contains one of <literal>ROW</>, <literal>TD["level"]</> contains one of <literal>ROW</>,
<literal>STATEMENT</>, and <literal>UNKNOWN</>. <literal>STATEMENT</>, or <literal>UNKNOWN</>.
For a row-level trigger, the trigger
rows are in <literal>TD["new"]</> and/or <literal>TD["old"]</>
depending on the trigger event.
<literal>TD["name"]</> contains the trigger name, <literal>TD["name"]</> contains the trigger name,
<literal>TD["table_name"]</> contains the name of the table on which the trigger occurred, <literal>TD["table_name"]</> contains the name of the table on which the trigger occurred,
<literal>TD["table_schema"]</> contains the schema of the table on which the trigger occurred, <literal>TD["table_schema"]</> contains the schema of the table on which the trigger occurred,
<literal>TD["name"]</> contains the trigger name, and and <literal>TD["relid"]</> contains the OID of the table on
<literal>TD["relid"]</> contains the OID of the table on
which the trigger occurred. If the <command>CREATE TRIGGER</> command which the trigger occurred. If the <command>CREATE TRIGGER</> command
included arguments, they are available in <literal>TD["args"][0]</> to included arguments, they are available in <literal>TD["args"][0]</> to
<literal>TD["args"][(<replaceable>n</>-1)]</>. <literal>TD["args"][<replaceable>n</>-1]</>.
</para> </para>
<para> <para>
If <literal>TD["when"]</literal> is <literal>BEFORE</>, you can If <literal>TD["when"]</literal> is <literal>BEFORE</> and
<literal>TD["level"]</literal> is <literal>ROW</>, you can
return <literal>None</literal> or <literal>"OK"</literal> from the return <literal>None</literal> or <literal>"OK"</literal> from the
Python function to indicate the row is unmodified, Python function to indicate the row is unmodified,
<literal>"SKIP"</> to abort the event, or <literal>"MODIFY"</> to <literal>"SKIP"</> to abort the event, or <literal>"MODIFY"</> to
indicate you've modified the row. indicate you've modified the row.
Otherwise the return value is ignored.
</para> </para>
</sect1> </sect1>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/pltcl.sgml,v 2.47 2007/12/03 23:49:50 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/pltcl.sgml,v 2.48 2008/03/28 00:21:55 tgl Exp $ -->
<chapter id="pltcl"> <chapter id="pltcl">
<title>PL/Tcl - Tcl Procedural Language</title> <title>PL/Tcl - Tcl Procedural Language</title>
...@@ -569,7 +569,7 @@ SELECT 'doesn''t' AS ret ...@@ -569,7 +569,7 @@ SELECT 'doesn''t' AS ret
<listitem> <listitem>
<para> <para>
The string <literal>BEFORE</> or <literal>AFTER</> depending on the The string <literal>BEFORE</> or <literal>AFTER</> depending on the
type of trigger call. type of trigger event.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -579,7 +579,7 @@ SELECT 'doesn''t' AS ret ...@@ -579,7 +579,7 @@ SELECT 'doesn''t' AS ret
<listitem> <listitem>
<para> <para>
The string <literal>ROW</> or <literal>STATEMENT</> depending on the The string <literal>ROW</> or <literal>STATEMENT</> depending on the
type of trigger call. type of trigger event.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -588,8 +588,9 @@ SELECT 'doesn''t' AS ret ...@@ -588,8 +588,9 @@ SELECT 'doesn''t' AS ret
<term><varname>$TG_op</varname></term> <term><varname>$TG_op</varname></term>
<listitem> <listitem>
<para> <para>
The string <literal>INSERT</>, <literal>UPDATE</>, or The string <literal>INSERT</>, <literal>UPDATE</>,
<literal>DELETE</> depending on the type of trigger call. <literal>DELETE</>, or <literal>TRUNCATE</> depending on the type of
trigger event.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -602,6 +603,7 @@ SELECT 'doesn''t' AS ret ...@@ -602,6 +603,7 @@ SELECT 'doesn''t' AS ret
row for <command>INSERT</> or <command>UPDATE</> actions, or row for <command>INSERT</> or <command>UPDATE</> actions, or
empty for <command>DELETE</>. The array is indexed by column empty for <command>DELETE</>. The array is indexed by column
name. Columns that are null will not appear in the array. name. Columns that are null will not appear in the array.
This is not set for statement-level triggers.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -614,6 +616,7 @@ SELECT 'doesn''t' AS ret ...@@ -614,6 +616,7 @@ SELECT 'doesn''t' AS ret
row for <command>UPDATE</> or <command>DELETE</> actions, or row for <command>UPDATE</> or <command>DELETE</> actions, or
empty for <command>INSERT</>. The array is indexed by column empty for <command>INSERT</>. The array is indexed by column
name. Columns that are null will not appear in the array. name. Columns that are null will not appear in the array.
This is not set for statement-level triggers.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -644,6 +647,7 @@ SELECT 'doesn''t' AS ret ...@@ -644,6 +647,7 @@ SELECT 'doesn''t' AS ret
only.) Needless to say that all this is only meaningful when the trigger only.) Needless to say that all this is only meaningful when the trigger
is <literal>BEFORE</> and <command>FOR EACH ROW</>; otherwise the return value is ignored. is <literal>BEFORE</> and <command>FOR EACH ROW</>; otherwise the return value is ignored.
</para> </para>
<para> <para>
Here's a little example trigger procedure that forces an integer value Here's a little example trigger procedure that forces an integer value
in a table to keep track of the number of updates that are performed on the in a table to keep track of the number of updates that are performed on the
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_trigger.sgml,v 1.47 2007/02/01 19:10:24 momjian Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/create_trigger.sgml,v 1.48 2008/03/28 00:21:55 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -25,7 +25,7 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE ...@@ -25,7 +25,7 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
EXECUTE PROCEDURE <replaceable class="PARAMETER">funcname</replaceable> ( <replaceable class="PARAMETER">arguments</replaceable> ) EXECUTE PROCEDURE <replaceable class="PARAMETER">funcname</replaceable> ( <replaceable class="PARAMETER">arguments</replaceable> )
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
<refsect1> <refsect1>
<title>Description</title> <title>Description</title>
...@@ -65,6 +65,12 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE ...@@ -65,6 +65,12 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
EACH STATEMENT</literal> triggers). EACH STATEMENT</literal> triggers).
</para> </para>
<para>
In addition, triggers may be defined to fire for a
<command>TRUNCATE</command>, though only
<literal>FOR EACH STATEMENT</literal>.
</para>
<para> <para>
If multiple triggers of the same kind are defined for the same event, If multiple triggers of the same kind are defined for the same event,
they will be fired in alphabetical order by name. they will be fired in alphabetical order by name.
...@@ -80,7 +86,7 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE ...@@ -80,7 +86,7 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
Refer to <xref linkend="triggers"> for more information about triggers. Refer to <xref linkend="triggers"> for more information about triggers.
</para> </para>
</refsect1> </refsect1>
<refsect1> <refsect1>
<title>Parameters</title> <title>Parameters</title>
...@@ -110,10 +116,10 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE ...@@ -110,10 +116,10 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
<term><replaceable class="parameter">event</replaceable></term> <term><replaceable class="parameter">event</replaceable></term>
<listitem> <listitem>
<para> <para>
One of <command>INSERT</command>, <command>UPDATE</command>, or One of <command>INSERT</command>, <command>UPDATE</command>,
<command>DELETE</command>; this specifies the event that will <command>DELETE</command>, or <command>TRUNCATE</command>;
fire the trigger. Multiple events can be specified using this specifies the event that will fire the trigger. Multiple
<literal>OR</literal>. events can be specified using <literal>OR</literal>.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -179,6 +185,11 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE ...@@ -179,6 +185,11 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
<literal>TRIGGER</literal> privilege on the table. <literal>TRIGGER</literal> privilege on the table.
</para> </para>
<para>
Use <xref linkend="sql-droptrigger"
endterm="sql-droptrigger-title"> to remove a trigger.
</para>
<para> <para>
In <productname>PostgreSQL</productname> versions before 7.3, it was In <productname>PostgreSQL</productname> versions before 7.3, it was
necessary to declare trigger functions as returning the placeholder necessary to declare trigger functions as returning the placeholder
...@@ -187,11 +198,6 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE ...@@ -187,11 +198,6 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
declared as returning <type>opaque</>, but it will issue a notice and declared as returning <type>opaque</>, but it will issue a notice and
change the function's declared return type to <type>trigger</>. change the function's declared return type to <type>trigger</>.
</para> </para>
<para>
Use <xref linkend="sql-droptrigger"
endterm="sql-droptrigger-title"> to remove a trigger.
</para>
</refsect1> </refsect1>
<refsect1 id="R1-SQL-CREATETRIGGER-2"> <refsect1 id="R1-SQL-CREATETRIGGER-2">
...@@ -204,7 +210,7 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE ...@@ -204,7 +210,7 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
<refsect1 id="SQL-CREATETRIGGER-compatibility"> <refsect1 id="SQL-CREATETRIGGER-compatibility">
<title>Compatibility</title> <title>Compatibility</title>
<para> <para>
The <command>CREATE TRIGGER</command> statement in The <command>CREATE TRIGGER</command> statement in
<productname>PostgreSQL</productname> implements a subset of the <productname>PostgreSQL</productname> implements a subset of the
...@@ -267,6 +273,12 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE ...@@ -267,6 +273,12 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
<literal>OR</literal> is a <productname>PostgreSQL</> extension of <literal>OR</literal> is a <productname>PostgreSQL</> extension of
the SQL standard. the SQL standard.
</para> </para>
<para>
The ability to fire triggers for <command>TRUNCATE</command> is a
<productname>PostgreSQL</> extension of the SQL standard.
</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.24 2007/05/11 19:40:08 neilc Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.25 2008/03/28 00:21:55 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -36,7 +36,7 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ C ...@@ -36,7 +36,7 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ C
operation. This is most useful on large tables. operation. This is most useful on large tables.
</para> </para>
</refsect1> </refsect1>
<refsect1> <refsect1>
<title>Parameters</title> <title>Parameters</title>
...@@ -91,8 +91,16 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ C ...@@ -91,8 +91,16 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ C
</para> </para>
<para> <para>
<command>TRUNCATE</> will not run any <literal>ON DELETE</literal> <command>TRUNCATE</> will not fire any <literal>ON DELETE</literal>
triggers that might exist for the tables. triggers that might exist for the tables. But it will fire
<literal>ON TRUNCATE</literal> triggers.
If <literal>ON TRUNCATE</> triggers are defined for any of
the tables, then all <literal>BEFORE TRUNCATE</literal> triggers are
fired before any truncation happens, and all <literal>AFTER
TRUNCATE</literal> triggers are fired after the last truncation is
performed. The triggers will fire in the order that the tables are
to be processed (first those listed in the command, and then any
that were added due to cascading).
</para> </para>
<warning> <warning>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/trigger.sgml,v 1.51 2007/12/03 23:49:51 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/trigger.sgml,v 1.52 2008/03/28 00:21:55 tgl Exp $ -->
<chapter id="triggers"> <chapter id="triggers">
<title>Triggers</title> <title>Triggers</title>
...@@ -36,14 +36,15 @@ ...@@ -36,14 +36,15 @@
performed. Triggers can be defined to execute either before or after any performed. Triggers can be defined to execute either before or after any
<command>INSERT</command>, <command>UPDATE</command>, or <command>INSERT</command>, <command>UPDATE</command>, or
<command>DELETE</command> operation, either once per modified row, <command>DELETE</command> operation, either once per modified row,
or once per <acronym>SQL</acronym> statement. or once per <acronym>SQL</acronym> statement. Triggers can also fire
If a trigger event occurs, the trigger's function is called for <command>TRUNCATE</command> statements. If a trigger event occurs,
at the appropriate time to handle the event. the trigger's function is called at the appropriate time to handle the
event.
</para> </para>
<para> <para>
The trigger function must be defined before the trigger itself can be The trigger function must be defined before the trigger itself can be
created. The trigger function must be declared as a created. The trigger function must be declared as a
function taking no arguments and returning type <literal>trigger</>. function taking no arguments and returning type <literal>trigger</>.
(The trigger function receives its input through a specially-passed (The trigger function receives its input through a specially-passed
<structname>TriggerData</> structure, not in the form of ordinary function <structname>TriggerData</> structure, not in the form of ordinary function
...@@ -69,7 +70,8 @@ ...@@ -69,7 +70,8 @@
in the execution of any applicable per-statement triggers. These in the execution of any applicable per-statement triggers. These
two types of triggers are sometimes called <firstterm>row-level</> two types of triggers are sometimes called <firstterm>row-level</>
triggers and <firstterm>statement-level</> triggers, triggers and <firstterm>statement-level</> triggers,
respectively. respectively. Triggers on <command>TRUNCATE</command> may only be
defined at statement-level.
</para> </para>
<para> <para>
...@@ -398,6 +400,15 @@ typedef struct TriggerData ...@@ -398,6 +400,15 @@ typedef struct TriggerData
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><literal>TRIGGER_FIRED_BY_TRUNCATE(tg_event)</literal></term>
<listitem>
<para>
Returns true if the trigger was fired by a <command>TRUNCATE</command> command.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</para> </para>
</listitem> </listitem>
...@@ -630,10 +641,10 @@ CREATE FUNCTION trigf() RETURNS trigger ...@@ -630,10 +641,10 @@ CREATE FUNCTION trigf() RETURNS trigger
AS '<replaceable>filename</>' AS '<replaceable>filename</>'
LANGUAGE C; LANGUAGE C;
CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest
FOR EACH ROW EXECUTE PROCEDURE trigf(); FOR EACH ROW EXECUTE PROCEDURE trigf();
CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest
FOR EACH ROW EXECUTE PROCEDURE trigf(); FOR EACH ROW EXECUTE PROCEDURE trigf();
</programlisting> </programlisting>
</para> </para>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.248 2008/03/27 03:57:33 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.249 2008/03/28 00:21:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -539,6 +539,9 @@ ExecuteTruncate(TruncateStmt *stmt) ...@@ -539,6 +539,9 @@ ExecuteTruncate(TruncateStmt *stmt)
{ {
List *rels = NIL; List *rels = NIL;
List *relids = NIL; List *relids = NIL;
EState *estate;
ResultRelInfo *resultRelInfos;
ResultRelInfo *resultRelInfo;
ListCell *cell; ListCell *cell;
/* /*
...@@ -601,6 +604,45 @@ ExecuteTruncate(TruncateStmt *stmt) ...@@ -601,6 +604,45 @@ ExecuteTruncate(TruncateStmt *stmt)
heap_truncate_check_FKs(rels, false); heap_truncate_check_FKs(rels, false);
#endif #endif
/* Prepare to catch AFTER triggers. */
AfterTriggerBeginQuery();
/*
* To fire triggers, we'll need an EState as well as a ResultRelInfo
* for each relation.
*/
estate = CreateExecutorState();
resultRelInfos = (ResultRelInfo *)
palloc(list_length(rels) * sizeof(ResultRelInfo));
resultRelInfo = resultRelInfos;
foreach(cell, rels)
{
Relation rel = (Relation) lfirst(cell);
InitResultRelInfo(resultRelInfo,
rel,
0, /* dummy rangetable index */
CMD_DELETE, /* don't need any index info */
false);
resultRelInfo++;
}
estate->es_result_relations = resultRelInfos;
estate->es_num_result_relations = list_length(rels);
/*
* Process all BEFORE STATEMENT TRUNCATE triggers before we begin
* truncating (this is because one of them might throw an error).
* Also, if we were to allow them to prevent statement execution,
* that would need to be handled here.
*/
resultRelInfo = resultRelInfos;
foreach(cell, rels)
{
estate->es_result_relation_info = resultRelInfo;
ExecBSTruncateTriggers(estate, resultRelInfo);
resultRelInfo++;
}
/* /*
* OK, truncate each table. * OK, truncate each table.
*/ */
...@@ -637,6 +679,23 @@ ExecuteTruncate(TruncateStmt *stmt) ...@@ -637,6 +679,23 @@ ExecuteTruncate(TruncateStmt *stmt)
*/ */
reindex_relation(heap_relid, true); reindex_relation(heap_relid, true);
} }
/*
* Process all AFTER STATEMENT TRUNCATE triggers.
*/
resultRelInfo = resultRelInfos;
foreach(cell, rels)
{
estate->es_result_relation_info = resultRelInfo;
ExecASTruncateTriggers(estate, resultRelInfo);
resultRelInfo++;
}
/* Handle queued AFTER triggers */
AfterTriggerEndQuery(estate);
/* We can clean up the EState now */
FreeExecutorState(estate);
} }
/* /*
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.230 2008/03/26 21:10:38 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.231 2008/03/28 00:21:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -179,6 +179,18 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid) ...@@ -179,6 +179,18 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid)
errmsg("multiple UPDATE events specified"))); errmsg("multiple UPDATE events specified")));
TRIGGER_SETT_UPDATE(tgtype); TRIGGER_SETT_UPDATE(tgtype);
break; break;
case 't':
if (TRIGGER_FOR_TRUNCATE(tgtype))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("multiple TRUNCATE events specified")));
TRIGGER_SETT_TRUNCATE(tgtype);
/* Disallow ROW-level TRUNCATE triggers */
if (stmt->row)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("TRUNCATE FOR EACH ROW triggers are not supported")));
break;
default: default:
elog(ERROR, "unrecognized trigger event: %d", elog(ERROR, "unrecognized trigger event: %d",
(int) stmt->actions[i]); (int) stmt->actions[i]);
...@@ -1299,6 +1311,15 @@ InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx) ...@@ -1299,6 +1311,15 @@ InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx)
(*tp)[n[TRIGGER_EVENT_UPDATE]] = indx; (*tp)[n[TRIGGER_EVENT_UPDATE]] = indx;
(n[TRIGGER_EVENT_UPDATE])++; (n[TRIGGER_EVENT_UPDATE])++;
} }
if (TRIGGER_FOR_TRUNCATE(trigger->tgtype))
{
tp = &(t[TRIGGER_EVENT_TRUNCATE]);
if (*tp == NULL)
*tp = (int *) palloc(trigdesc->numtriggers * sizeof(int));
(*tp)[n[TRIGGER_EVENT_TRUNCATE]] = indx;
(n[TRIGGER_EVENT_TRUNCATE])++;
}
} }
/* /*
...@@ -2030,6 +2051,75 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ...@@ -2030,6 +2051,75 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
} }
} }
void
ExecBSTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
{
TriggerDesc *trigdesc;
int ntrigs;
int *tgindx;
int i;
TriggerData LocTriggerData;
trigdesc = relinfo->ri_TrigDesc;
if (trigdesc == NULL)
return;
ntrigs = trigdesc->n_before_statement[TRIGGER_EVENT_TRUNCATE];
tgindx = trigdesc->tg_before_statement[TRIGGER_EVENT_TRUNCATE];
if (ntrigs == 0)
return;
LocTriggerData.type = T_TriggerData;
LocTriggerData.tg_event = TRIGGER_EVENT_TRUNCATE |
TRIGGER_EVENT_BEFORE;
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
LocTriggerData.tg_trigtuple = NULL;
LocTriggerData.tg_newtuple = NULL;
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_newtuplebuf = InvalidBuffer;
for (i = 0; i < ntrigs; i++)
{
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
HeapTuple newtuple;
if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
{
if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN ||
trigger->tgenabled == TRIGGER_DISABLED)
continue;
}
else /* ORIGIN or LOCAL role */
{
if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA ||
trigger->tgenabled == TRIGGER_DISABLED)
continue;
}
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
tgindx[i],
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate));
if (newtuple)
ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
errmsg("BEFORE STATEMENT trigger cannot return a value")));
}
}
void
ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_TRUNCATE] > 0)
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_TRUNCATE,
false, NULL, NULL);
}
static HeapTuple static HeapTuple
GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo, GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo,
...@@ -3571,6 +3661,12 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, ...@@ -3571,6 +3661,12 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
if (afterTriggers == NULL) if (afterTriggers == NULL)
elog(ERROR, "AfterTriggerSaveEvent() called outside of transaction"); elog(ERROR, "AfterTriggerSaveEvent() called outside of transaction");
/*
* event is used both as a bitmask and an array offset,
* so make sure we don't walk off the edge of our arrays
*/
Assert(event >= 0 && event < TRIGGER_NUM_EVENT_CLASSES);
/* /*
* Get the CTID's of OLD and NEW * Get the CTID's of OLD and NEW
*/ */
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.304 2008/03/26 21:10:38 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.305 2008/03/28 00:21:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -66,11 +66,6 @@ typedef struct evalPlanQual ...@@ -66,11 +66,6 @@ typedef struct evalPlanQual
/* decls for local routines only used within this module */ /* decls for local routines only used within this module */
static void InitPlan(QueryDesc *queryDesc, int eflags); static void InitPlan(QueryDesc *queryDesc, int eflags);
static void initResultRelInfo(ResultRelInfo *resultRelInfo,
Relation resultRelationDesc,
Index resultRelationIndex,
CmdType operation,
bool doInstrument);
static void ExecEndPlan(PlanState *planstate, EState *estate); static void ExecEndPlan(PlanState *planstate, EState *estate);
static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate, static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate,
CmdType operation, CmdType operation,
...@@ -525,7 +520,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) ...@@ -525,7 +520,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
resultRelationOid = getrelid(resultRelationIndex, rangeTable); resultRelationOid = getrelid(resultRelationIndex, rangeTable);
resultRelation = heap_open(resultRelationOid, RowExclusiveLock); resultRelation = heap_open(resultRelationOid, RowExclusiveLock);
initResultRelInfo(resultRelInfo, InitResultRelInfo(resultRelInfo,
resultRelation, resultRelation,
resultRelationIndex, resultRelationIndex,
operation, operation,
...@@ -860,8 +855,8 @@ InitPlan(QueryDesc *queryDesc, int eflags) ...@@ -860,8 +855,8 @@ InitPlan(QueryDesc *queryDesc, int eflags)
/* /*
* Initialize ResultRelInfo data for one result relation * Initialize ResultRelInfo data for one result relation
*/ */
static void void
initResultRelInfo(ResultRelInfo *resultRelInfo, InitResultRelInfo(ResultRelInfo *resultRelInfo,
Relation resultRelationDesc, Relation resultRelationDesc,
Index resultRelationIndex, Index resultRelationIndex,
CmdType operation, CmdType operation,
...@@ -997,11 +992,11 @@ ExecGetTriggerResultRel(EState *estate, Oid relid) ...@@ -997,11 +992,11 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
/* /*
* Make the new entry in the right context. Currently, we don't need any * Make the new entry in the right context. Currently, we don't need any
* index information in ResultRelInfos used only for triggers, so tell * index information in ResultRelInfos used only for triggers, so tell
* initResultRelInfo it's a DELETE. * InitResultRelInfo it's a DELETE.
*/ */
oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
rInfo = makeNode(ResultRelInfo); rInfo = makeNode(ResultRelInfo);
initResultRelInfo(rInfo, InitResultRelInfo(rInfo,
rel, rel,
0, /* dummy rangetable index */ 0, /* dummy rangetable index */
CMD_DELETE, CMD_DELETE,
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.610 2008/03/21 22:41:48 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.611 2008/03/28 00:21:55 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -2719,6 +2719,7 @@ TriggerOneEvent: ...@@ -2719,6 +2719,7 @@ TriggerOneEvent:
INSERT { $$ = 'i'; } INSERT { $$ = 'i'; }
| DELETE_P { $$ = 'd'; } | DELETE_P { $$ = 'd'; }
| UPDATE { $$ = 'u'; } | UPDATE { $$ = 'u'; }
| TRUNCATE { $$ = 't'; }
; ;
TriggerForSpec: TriggerForSpec:
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.271 2008/03/26 21:10:39 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.272 2008/03/28 00:21:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -499,6 +499,13 @@ pg_get_triggerdef(PG_FUNCTION_ARGS) ...@@ -499,6 +499,13 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
else else
appendStringInfo(&buf, " UPDATE"); appendStringInfo(&buf, " UPDATE");
} }
if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
{
if (findx > 0)
appendStringInfo(&buf, " OR TRUNCATE");
else
appendStringInfo(&buf, " TRUNCATE");
}
appendStringInfo(&buf, " ON %s ", appendStringInfo(&buf, " ON %s ",
generate_relation_name(trigrec->tgrelid)); generate_relation_name(trigrec->tgrelid));
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* by PostgreSQL * by PostgreSQL
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.485 2008/03/27 03:57:33 tgl Exp $ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.486 2008/03/28 00:21:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -9631,6 +9631,13 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) ...@@ -9631,6 +9631,13 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
else else
appendPQExpBuffer(query, " UPDATE"); appendPQExpBuffer(query, " UPDATE");
} }
if (TRIGGER_FOR_TRUNCATE(tginfo->tgtype))
{
if (findx > 0)
appendPQExpBuffer(query, " OR TRUNCATE");
else
appendPQExpBuffer(query, " TRUNCATE");
}
appendPQExpBuffer(query, " ON %s\n", appendPQExpBuffer(query, " ON %s\n",
fmtId(tbinfo->dobj.name)); fmtId(tbinfo->dobj.name));
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/pg_trigger.h,v 1.31 2008/03/27 03:57:34 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/pg_trigger.h,v 1.32 2008/03/28 00:21:56 tgl Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -89,6 +89,7 @@ typedef FormData_pg_trigger *Form_pg_trigger; ...@@ -89,6 +89,7 @@ typedef FormData_pg_trigger *Form_pg_trigger;
#define TRIGGER_TYPE_INSERT (1 << 2) #define TRIGGER_TYPE_INSERT (1 << 2)
#define TRIGGER_TYPE_DELETE (1 << 3) #define TRIGGER_TYPE_DELETE (1 << 3)
#define TRIGGER_TYPE_UPDATE (1 << 4) #define TRIGGER_TYPE_UPDATE (1 << 4)
#define TRIGGER_TYPE_TRUNCATE (1 << 5)
/* Macros for manipulating tgtype */ /* Macros for manipulating tgtype */
#define TRIGGER_CLEAR_TYPE(type) ((type) = 0) #define TRIGGER_CLEAR_TYPE(type) ((type) = 0)
...@@ -98,11 +99,13 @@ typedef FormData_pg_trigger *Form_pg_trigger; ...@@ -98,11 +99,13 @@ typedef FormData_pg_trigger *Form_pg_trigger;
#define TRIGGER_SETT_INSERT(type) ((type) |= TRIGGER_TYPE_INSERT) #define TRIGGER_SETT_INSERT(type) ((type) |= TRIGGER_TYPE_INSERT)
#define TRIGGER_SETT_DELETE(type) ((type) |= TRIGGER_TYPE_DELETE) #define TRIGGER_SETT_DELETE(type) ((type) |= TRIGGER_TYPE_DELETE)
#define TRIGGER_SETT_UPDATE(type) ((type) |= TRIGGER_TYPE_UPDATE) #define TRIGGER_SETT_UPDATE(type) ((type) |= TRIGGER_TYPE_UPDATE)
#define TRIGGER_SETT_TRUNCATE(type) ((type) |= TRIGGER_TYPE_TRUNCATE)
#define TRIGGER_FOR_ROW(type) ((type) & TRIGGER_TYPE_ROW) #define TRIGGER_FOR_ROW(type) ((type) & TRIGGER_TYPE_ROW)
#define TRIGGER_FOR_BEFORE(type) ((type) & TRIGGER_TYPE_BEFORE) #define TRIGGER_FOR_BEFORE(type) ((type) & TRIGGER_TYPE_BEFORE)
#define TRIGGER_FOR_INSERT(type) ((type) & TRIGGER_TYPE_INSERT) #define TRIGGER_FOR_INSERT(type) ((type) & TRIGGER_TYPE_INSERT)
#define TRIGGER_FOR_DELETE(type) ((type) & TRIGGER_TYPE_DELETE) #define TRIGGER_FOR_DELETE(type) ((type) & TRIGGER_TYPE_DELETE)
#define TRIGGER_FOR_UPDATE(type) ((type) & TRIGGER_TYPE_UPDATE) #define TRIGGER_FOR_UPDATE(type) ((type) & TRIGGER_TYPE_UPDATE)
#define TRIGGER_FOR_TRUNCATE(type) ((type) & TRIGGER_TYPE_TRUNCATE)
#endif /* PG_TRIGGER_H */ #endif /* PG_TRIGGER_H */
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.66 2008/01/02 23:34:42 tgl Exp $ * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.67 2008/03/28 00:21:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -38,11 +38,18 @@ typedef struct TriggerData ...@@ -38,11 +38,18 @@ typedef struct TriggerData
Buffer tg_newtuplebuf; Buffer tg_newtuplebuf;
} TriggerData; } TriggerData;
/* TriggerEvent bit flags */ /*
* TriggerEvent bit flags
*
* Note that we assume different event types (INSERT/DELETE/UPDATE/TRUNCATE)
* can't be OR'd together in a single TriggerEvent. This is unlike the
* situation for pg_trigger rows, so pg_trigger.tgtype uses a different
* representation!
*/
#define TRIGGER_EVENT_INSERT 0x00000000 #define TRIGGER_EVENT_INSERT 0x00000000
#define TRIGGER_EVENT_DELETE 0x00000001 #define TRIGGER_EVENT_DELETE 0x00000001
#define TRIGGER_EVENT_UPDATE 0x00000002 #define TRIGGER_EVENT_UPDATE 0x00000002
#define TRIGGER_EVENT_TRUNCATE 0x00000003
#define TRIGGER_EVENT_OPMASK 0x00000003 #define TRIGGER_EVENT_OPMASK 0x00000003
#define TRIGGER_EVENT_ROW 0x00000004 #define TRIGGER_EVENT_ROW 0x00000004
#define TRIGGER_EVENT_BEFORE 0x00000008 #define TRIGGER_EVENT_BEFORE 0x00000008
...@@ -66,6 +73,10 @@ typedef struct TriggerData ...@@ -66,6 +73,10 @@ typedef struct TriggerData
(((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \ (((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \
TRIGGER_EVENT_UPDATE) TRIGGER_EVENT_UPDATE)
#define TRIGGER_FIRED_BY_TRUNCATE(event) \
(((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \
TRIGGER_EVENT_TRUNCATE)
#define TRIGGER_FIRED_FOR_ROW(event) \ #define TRIGGER_FIRED_FOR_ROW(event) \
((TriggerEvent) (event) & TRIGGER_EVENT_ROW) ((TriggerEvent) (event) & TRIGGER_EVENT_ROW)
...@@ -140,6 +151,10 @@ extern void ExecARUpdateTriggers(EState *estate, ...@@ -140,6 +151,10 @@ extern void ExecARUpdateTriggers(EState *estate,
ResultRelInfo *relinfo, ResultRelInfo *relinfo,
ItemPointer tupleid, ItemPointer tupleid,
HeapTuple newtuple); HeapTuple newtuple);
extern void ExecBSTruncateTriggers(EState *estate,
ResultRelInfo *relinfo);
extern void ExecASTruncateTriggers(EState *estate,
ResultRelInfo *relinfo);
extern void AfterTriggerBeginXact(void); extern void AfterTriggerBeginXact(void);
extern void AfterTriggerBeginQuery(void); extern void AfterTriggerBeginQuery(void);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.146 2008/01/01 19:45:57 momjian Exp $ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.147 2008/03/28 00:21:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -138,6 +138,11 @@ extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, ...@@ -138,6 +138,11 @@ extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc,
ScanDirection direction, long count); ScanDirection direction, long count);
extern void ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc);
extern void ExecutorRewind(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc);
extern void InitResultRelInfo(ResultRelInfo *resultRelInfo,
Relation resultRelationDesc,
Index resultRelationIndex,
CmdType operation,
bool doInstrument);
extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid);
extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
extern void ExecConstraints(ResultRelInfo *resultRelInfo, extern void ExecConstraints(ResultRelInfo *resultRelInfo,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.104 2008/01/01 19:45:59 momjian Exp $ * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.105 2008/03/28 00:21:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -71,9 +71,10 @@ typedef struct TriggerDesc ...@@ -71,9 +71,10 @@ typedef struct TriggerDesc
/* /*
* Index data to identify which triggers are which. Since each trigger * Index data to identify which triggers are which. Since each trigger
* can appear in more than one class, for each class we provide a list of * can appear in more than one class, for each class we provide a list of
* integer indexes into the triggers array. * integer indexes into the triggers array. The class codes are defined
* by TRIGGER_EVENT_xxx macros in commands/trigger.h.
*/ */
#define TRIGGER_NUM_EVENT_CLASSES 3 #define TRIGGER_NUM_EVENT_CLASSES 4
uint16 n_before_statement[TRIGGER_NUM_EVENT_CLASSES]; uint16 n_before_statement[TRIGGER_NUM_EVENT_CLASSES];
uint16 n_before_row[TRIGGER_NUM_EVENT_CLASSES]; uint16 n_before_row[TRIGGER_NUM_EVENT_CLASSES];
......
/********************************************************************** /**********************************************************************
* plperl.c - perl as a procedural language for PostgreSQL * plperl.c - perl as a procedural language for PostgreSQL
* *
* $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.138 2008/03/25 22:42:45 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.139 2008/03/28 00:21:56 tgl Exp $
* *
**********************************************************************/ **********************************************************************/
...@@ -689,6 +689,8 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo) ...@@ -689,6 +689,8 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo)
tupdesc)); tupdesc));
} }
} }
else if (TRIGGER_FIRED_BY_TRUNCATE(tdata->tg_event))
event = "TRUNCATE";
else else
event = "UNKNOWN"; event = "UNKNOWN";
...@@ -1395,6 +1397,8 @@ plperl_trigger_handler(PG_FUNCTION_ARGS) ...@@ -1395,6 +1397,8 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
retval = (Datum) trigdata->tg_newtuple; retval = (Datum) trigdata->tg_newtuple;
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
retval = (Datum) trigdata->tg_trigtuple; retval = (Datum) trigdata->tg_trigtuple;
else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
retval = (Datum) trigdata->tg_trigtuple;
else else
retval = (Datum) 0; /* can this happen? */ retval = (Datum) 0; /* can this happen? */
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.206 2008/03/26 18:48:59 alvherre Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.207 2008/03/28 00:21:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -538,8 +538,10 @@ plpgsql_exec_trigger(PLpgSQL_function *func, ...@@ -538,8 +538,10 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
var->value = CStringGetTextDatum("UPDATE"); var->value = CStringGetTextDatum("UPDATE");
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
var->value = CStringGetTextDatum("DELETE"); var->value = CStringGetTextDatum("DELETE");
else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
var->value = CStringGetTextDatum("TRUNCATE");
else else
elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE"); elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, UPDATE, or TRUNCATE");
var->isnull = false; var->isnull = false;
var->freeval = true; var->freeval = true;
......
/********************************************************************** /**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL * plpython.c - python as a procedural language for PostgreSQL
* *
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.107 2008/03/25 22:42:45 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.108 2008/03/28 00:21:56 tgl Exp $
* *
********************************************************************* *********************************************************************
*/ */
...@@ -714,6 +714,8 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple * ...@@ -714,6 +714,8 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
pltevent = PyString_FromString("DELETE"); pltevent = PyString_FromString("DELETE");
else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event)) else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
pltevent = PyString_FromString("UPDATE"); pltevent = PyString_FromString("UPDATE");
else if (TRIGGER_FIRED_BY_TRUNCATE(tdata->tg_event))
pltevent = PyString_FromString("TRUNCATE");
else else
{ {
elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event); elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event);
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* pltcl.c - PostgreSQL support for Tcl as * pltcl.c - PostgreSQL support for Tcl as
* procedural language (PL) * procedural language (PL)
* *
* $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.118 2008/03/25 22:42:46 tgl Exp $ * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.119 2008/03/28 00:21:56 tgl Exp $
* *
**********************************************************************/ **********************************************************************/
...@@ -824,6 +824,8 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) ...@@ -824,6 +824,8 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
Tcl_DStringAppendElement(&tcl_cmd, "DELETE"); Tcl_DStringAppendElement(&tcl_cmd, "DELETE");
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
Tcl_DStringAppendElement(&tcl_cmd, "UPDATE"); Tcl_DStringAppendElement(&tcl_cmd, "UPDATE");
else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
Tcl_DStringAppendElement(&tcl_cmd, "TRUNCATE");
else else
elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event); elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
......
...@@ -145,3 +145,81 @@ NOTICE: drop cascades to constraint trunc_e_a_fkey on table trunc_e ...@@ -145,3 +145,81 @@ NOTICE: drop cascades to constraint trunc_e_a_fkey on table trunc_e
NOTICE: drop cascades to constraint trunc_b_a_fkey on table trunc_b NOTICE: drop cascades to constraint trunc_b_a_fkey on table trunc_b
NOTICE: drop cascades to constraint trunc_e_b_fkey on table trunc_e NOTICE: drop cascades to constraint trunc_e_b_fkey on table trunc_e
NOTICE: drop cascades to constraint trunc_d_a_fkey on table trunc_d NOTICE: drop cascades to constraint trunc_d_a_fkey on table trunc_d
-- Test ON TRUNCATE triggers
CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text);
CREATE TABLE trunc_trigger_log (tgop text, tglevel text, tgwhen text,
tgargv text, tgtable name, rowcount bigint);
CREATE FUNCTION trunctrigger() RETURNS trigger as $$
declare c bigint;
begin
execute 'select count(*) from ' || quote_ident(tg_table_name) into c;
insert into trunc_trigger_log values
(TG_OP, TG_LEVEL, TG_WHEN, TG_ARGV[0], tg_table_name, c);
return null;
end;
$$ LANGUAGE plpgsql;
-- basic before trigger
INSERT INTO trunc_trigger_test VALUES(1, 'foo', 'bar'), (2, 'baz', 'quux');
CREATE TRIGGER t
BEFORE TRUNCATE ON trunc_trigger_test
FOR EACH STATEMENT
EXECUTE PROCEDURE trunctrigger('before trigger truncate');
SELECT count(*) as "Row count in test table" FROM trunc_trigger_test;
Row count in test table
-------------------------
2
(1 row)
SELECT * FROM trunc_trigger_log;
tgop | tglevel | tgwhen | tgargv | tgtable | rowcount
------+---------+--------+--------+---------+----------
(0 rows)
TRUNCATE trunc_trigger_test;
SELECT count(*) as "Row count in test table" FROM trunc_trigger_test;
Row count in test table
-------------------------
0
(1 row)
SELECT * FROM trunc_trigger_log;
tgop | tglevel | tgwhen | tgargv | tgtable | rowcount
----------+-----------+--------+-------------------------+--------------------+----------
TRUNCATE | STATEMENT | BEFORE | before trigger truncate | trunc_trigger_test | 2
(1 row)
DROP TRIGGER t ON trunc_trigger_test;
truncate trunc_trigger_log;
-- same test with an after trigger
INSERT INTO trunc_trigger_test VALUES(1, 'foo', 'bar'), (2, 'baz', 'quux');
CREATE TRIGGER tt
AFTER TRUNCATE ON trunc_trigger_test
FOR EACH STATEMENT
EXECUTE PROCEDURE trunctrigger('after trigger truncate');
SELECT count(*) as "Row count in test table" FROM trunc_trigger_test;
Row count in test table
-------------------------
2
(1 row)
SELECT * FROM trunc_trigger_log;
tgop | tglevel | tgwhen | tgargv | tgtable | rowcount
------+---------+--------+--------+---------+----------
(0 rows)
TRUNCATE trunc_trigger_test;
SELECT count(*) as "Row count in test table" FROM trunc_trigger_test;
Row count in test table
-------------------------
0
(1 row)
SELECT * FROM trunc_trigger_log;
tgop | tglevel | tgwhen | tgargv | tgtable | rowcount
----------+-----------+--------+------------------------+--------------------+----------
TRUNCATE | STATEMENT | AFTER | after trigger truncate | trunc_trigger_test | 0
(1 row)
DROP TABLE trunc_trigger_test;
DROP TABLE trunc_trigger_log;
DROP FUNCTION trunctrigger();
...@@ -77,3 +77,56 @@ SELECT * FROM truncate_a ...@@ -77,3 +77,56 @@ SELECT * FROM truncate_a
SELECT * FROM trunc_e; SELECT * FROM trunc_e;
DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE; DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE;
-- Test ON TRUNCATE triggers
CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text);
CREATE TABLE trunc_trigger_log (tgop text, tglevel text, tgwhen text,
tgargv text, tgtable name, rowcount bigint);
CREATE FUNCTION trunctrigger() RETURNS trigger as $$
declare c bigint;
begin
execute 'select count(*) from ' || quote_ident(tg_table_name) into c;
insert into trunc_trigger_log values
(TG_OP, TG_LEVEL, TG_WHEN, TG_ARGV[0], tg_table_name, c);
return null;
end;
$$ LANGUAGE plpgsql;
-- basic before trigger
INSERT INTO trunc_trigger_test VALUES(1, 'foo', 'bar'), (2, 'baz', 'quux');
CREATE TRIGGER t
BEFORE TRUNCATE ON trunc_trigger_test
FOR EACH STATEMENT
EXECUTE PROCEDURE trunctrigger('before trigger truncate');
SELECT count(*) as "Row count in test table" FROM trunc_trigger_test;
SELECT * FROM trunc_trigger_log;
TRUNCATE trunc_trigger_test;
SELECT count(*) as "Row count in test table" FROM trunc_trigger_test;
SELECT * FROM trunc_trigger_log;
DROP TRIGGER t ON trunc_trigger_test;
truncate trunc_trigger_log;
-- same test with an after trigger
INSERT INTO trunc_trigger_test VALUES(1, 'foo', 'bar'), (2, 'baz', 'quux');
CREATE TRIGGER tt
AFTER TRUNCATE ON trunc_trigger_test
FOR EACH STATEMENT
EXECUTE PROCEDURE trunctrigger('after trigger truncate');
SELECT count(*) as "Row count in test table" FROM trunc_trigger_test;
SELECT * FROM trunc_trigger_log;
TRUNCATE trunc_trigger_test;
SELECT count(*) as "Row count in test table" FROM trunc_trigger_test;
SELECT * FROM trunc_trigger_log;
DROP TABLE trunc_trigger_test;
DROP TABLE trunc_trigger_log;
DROP FUNCTION trunctrigger();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment