Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Abuhujair Javed
Postgres FD Implementation
Commits
7692d8d5
Commit
7692d8d5
authored
Mar 28, 2008
by
Tom Lane
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support statement-level ON TRUNCATE triggers. Simon Riggs
parent
107b3d0c
Changes
23
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
454 additions
and
83 deletions
+454
-83
doc/src/sgml/plperl.sgml
doc/src/sgml/plperl.sgml
+12
-9
doc/src/sgml/plpgsql.sgml
doc/src/sgml/plpgsql.sgml
+4
-4
doc/src/sgml/plpython.sgml
doc/src/sgml/plpython.sgml
+14
-11
doc/src/sgml/pltcl.sgml
doc/src/sgml/pltcl.sgml
+9
-5
doc/src/sgml/ref/create_trigger.sgml
doc/src/sgml/ref/create_trigger.sgml
+25
-13
doc/src/sgml/ref/truncate.sgml
doc/src/sgml/ref/truncate.sgml
+12
-4
doc/src/sgml/trigger.sgml
doc/src/sgml/trigger.sgml
+19
-8
src/backend/commands/tablecmds.c
src/backend/commands/tablecmds.c
+60
-1
src/backend/commands/trigger.c
src/backend/commands/trigger.c
+97
-1
src/backend/executor/execMain.c
src/backend/executor/execMain.c
+6
-11
src/backend/parser/gram.y
src/backend/parser/gram.y
+2
-1
src/backend/utils/adt/ruleutils.c
src/backend/utils/adt/ruleutils.c
+8
-1
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.c
+8
-1
src/include/catalog/pg_trigger.h
src/include/catalog/pg_trigger.h
+4
-1
src/include/commands/trigger.h
src/include/commands/trigger.h
+18
-3
src/include/executor/executor.h
src/include/executor/executor.h
+6
-1
src/include/utils/rel.h
src/include/utils/rel.h
+4
-3
src/pl/plperl/plperl.c
src/pl/plperl/plperl.c
+5
-1
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/pl_exec.c
+4
-2
src/pl/plpython/plpython.c
src/pl/plpython/plpython.c
+3
-1
src/pl/tcl/pltcl.c
src/pl/tcl/pltcl.c
+3
-1
src/test/regress/expected/truncate.out
src/test/regress/expected/truncate.out
+78
-0
src/test/regress/sql/truncate.sql
src/test/regress/sql/truncate.sql
+53
-0
No files found.
doc/src/sgml/plperl.sgml
View file @
7692d8d5
<!-- $PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.6
7 2008/01/25 15:28:35 adunstan
Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.6
8 2008/03/28 00:21:55 tgl
Exp $ -->
<chapter id="plperl">
<title>PL/Perl - Perl Procedural Language</title>
...
...
@@ -17,12 +17,14 @@
<ulink url="http://www.perl.com">Perl programming language</ulink>.
</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
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
string functions and control structures provided in PL/pgSQL.</para>
string functions and control structures provided in PL/pgSQL.
</para>
<para>
To install PL/Perl in a particular database, use
<literal>createlang plperl <replaceable>dbname</></literal>.
...
...
@@ -739,7 +741,8 @@ $$ LANGUAGE plperl;
<term><literal>$_TD->{event}</literal></term>
<listitem>
<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>
</listitem>
</varlistentry>
...
...
@@ -822,14 +825,14 @@ $$ LANGUAGE plperl;
</para>
<para>
T
riggers can return one of the following:
Row-level t
riggers can return one of the following:
<variablelist>
<varlistentry>
<term><literal>return;</literal></term>
<listitem>
<para>
Execute the
statement
Execute the
operation
</para>
</listitem>
</varlistentry>
...
...
@@ -838,7 +841,7 @@ $$ LANGUAGE plperl;
<term><literal>"SKIP"</literal></term>
<listitem>
<para>
Don't execute the
statement
Don't execute the
operation
</para>
</listitem>
</varlistentry>
...
...
doc/src/sgml/plpgsql.sgml
View file @
7692d8d5
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.12
4 2008/03/23 00:24:19
tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.12
5 2008/03/28 00:21:55
tgl Exp $ -->
<chapter id="plpgsql">
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
...
...
@@ -2785,9 +2785,9 @@ RAISE EXCEPTION 'Nonexistent ID --> %', user_id;
<listitem>
<para>
Data type <type>text</type>; a string of
<literal>INSERT</literal>, <literal>UPDATE</literal>,
or
<literal>DELETE</literal>
telling for which operation the
trigger was fired.
<literal>INSERT</literal>, <literal>UPDATE</literal>,
<literal>DELETE</literal>
, or <literal>TRUNCATE</>
t
elling for which operation the t
rigger was fired.
</para>
</listitem>
</varlistentry>
...
...
doc/src/sgml/plpython.sgml
View file @
7692d8d5
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpython.sgml,v 1.3
8 2007/02/01 00:28:17 momjian
Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpython.sgml,v 1.3
9 2008/03/28 00:21:55 tgl
Exp $ -->
<chapter id="plpython">
<title>PL/Python - Python Procedural Language</title>
...
...
@@ -381,31 +381,34 @@ $$ LANGUAGE plpythonu;
<para>
When a function is used as a trigger, the dictionary
<literal>TD</literal> contains trigger-related values. The trigger
rows are in <literal>TD["new"]</> and/or <literal>TD["old"]</>
depending on the trigger event. <literal>TD["event"]</> contains
<literal>TD</literal> contains trigger-related values.
<literal>TD["event"]</> contains
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>AFTER</>,
and
<literal>UNKNOWN</>.
<literal>AFTER</>,
or
<literal>UNKNOWN</>.
<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["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["name"]</> contains the trigger name, and
<literal>TD["relid"]</> contains the OID of the table on
and <literal>TD["relid"]</> contains the OID of the table on
which the trigger occurred. If the <command>CREATE TRIGGER</> command
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>
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
Python function to indicate the row is unmodified,
<literal>"SKIP"</> to abort the event, or <literal>"MODIFY"</> to
indicate you've modified the row.
Otherwise the return value is ignored.
</para>
</sect1>
...
...
doc/src/sgml/pltcl.sgml
View file @
7692d8d5
<!-- $PostgreSQL: pgsql/doc/src/sgml/pltcl.sgml,v 2.4
7 2007/12/03 23:49:50
tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/pltcl.sgml,v 2.4
8 2008/03/28 00:21:55
tgl Exp $ -->
<chapter id="pltcl">
<title>PL/Tcl - Tcl Procedural Language</title>
...
...
@@ -569,7 +569,7 @@ SELECT 'doesn''t' AS ret
<listitem>
<para>
The string <literal>BEFORE</> or <literal>AFTER</> depending on the
type of trigger
call
.
type of trigger
event
.
</para>
</listitem>
</varlistentry>
...
...
@@ -579,7 +579,7 @@ SELECT 'doesn''t' AS ret
<listitem>
<para>
The string <literal>ROW</> or <literal>STATEMENT</> depending on the
type of trigger
call
.
type of trigger
event
.
</para>
</listitem>
</varlistentry>
...
...
@@ -588,8 +588,9 @@ SELECT 'doesn''t' AS ret
<term><varname>$TG_op</varname></term>
<listitem>
<para>
The string <literal>INSERT</>, <literal>UPDATE</>, or
<literal>DELETE</> depending on the type of trigger call.
The string <literal>INSERT</>, <literal>UPDATE</>,
<literal>DELETE</>, or <literal>TRUNCATE</> depending on the type of
trigger event.
</para>
</listitem>
</varlistentry>
...
...
@@ -602,6 +603,7 @@ SELECT 'doesn''t' AS ret
row for <command>INSERT</> or <command>UPDATE</> actions, or
empty for <command>DELETE</>. The array is indexed by column
name. Columns that are null will not appear in the array.
This is not set for statement-level triggers.
</para>
</listitem>
</varlistentry>
...
...
@@ -614,6 +616,7 @@ SELECT 'doesn''t' AS ret
row for <command>UPDATE</> or <command>DELETE</> actions, or
empty for <command>INSERT</>. The array is indexed by column
name. Columns that are null will not appear in the array.
This is not set for statement-level triggers.
</para>
</listitem>
</varlistentry>
...
...
@@ -644,6 +647,7 @@ SELECT 'doesn''t' AS ret
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.
</para>
<para>
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
...
...
doc/src/sgml/ref/create_trigger.sgml
View file @
7692d8d5
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_trigger.sgml,v 1.4
7 2007/02/01 19:10:24 momjian
Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/create_trigger.sgml,v 1.4
8 2008/03/28 00:21:55 tgl
Exp $
PostgreSQL documentation
-->
...
...
@@ -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> )
</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
...
...
@@ -65,6 +65,12 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
EACH STATEMENT</literal> triggers).
</para>
<para>
In addition, triggers may be defined to fire for a
<command>TRUNCATE</command>, though only
<literal>FOR EACH STATEMENT</literal>.
</para>
<para>
If multiple triggers of the same kind are defined for the same event,
they will be fired in alphabetical order by name.
...
...
@@ -80,7 +86,7 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
Refer to <xref linkend="triggers"> for more information about triggers.
</para>
</refsect1>
<refsect1>
<title>Parameters</title>
...
...
@@ -110,10 +116,10 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
<term><replaceable class="parameter">event</replaceable></term>
<listitem>
<para>
One of <command>INSERT</command>, <command>UPDATE</command>,
or
<command>DELETE</command>
; this specifies the event that will
fire the trigger. Multiple events can be specified using
<literal>OR</literal>.
One of <command>INSERT</command>, <command>UPDATE</command>,
<command>DELETE</command>
, or <command>TRUNCATE</command>;
this specifies the event that will fire the trigger. Multiple
events can be specified using
<literal>OR</literal>.
</para>
</listitem>
</varlistentry>
...
...
@@ -179,6 +185,11 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
<literal>TRIGGER</literal> privilege on the table.
</para>
<para>
Use <xref linkend="sql-droptrigger"
endterm="sql-droptrigger-title"> to remove a trigger.
</para>
<para>
In <productname>PostgreSQL</productname> versions before 7.3, it was
necessary to declare trigger functions as returning the placeholder
...
...
@@ -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
change the function's declared return type to <type>trigger</>.
</para>
<para>
Use <xref linkend="sql-droptrigger"
endterm="sql-droptrigger-title"> to remove a trigger.
</para>
</refsect1>
<refsect1 id="R1-SQL-CREATETRIGGER-2">
...
...
@@ -204,7 +210,7 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
<refsect1 id="SQL-CREATETRIGGER-compatibility">
<title>Compatibility</title>
<para>
The <command>CREATE TRIGGER</command> statement in
<productname>PostgreSQL</productname> implements a subset of the
...
...
@@ -267,6 +273,12 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
<literal>OR</literal> is a <productname>PostgreSQL</> extension of
the SQL standard.
</para>
<para>
The ability to fire triggers for <command>TRUNCATE</command> is a
<productname>PostgreSQL</> extension of the SQL standard.
</para>
</refsect1>
<refsect1>
...
...
doc/src/sgml/ref/truncate.sgml
View file @
7692d8d5
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.2
4 2007/05/11 19:40:08 neilc
Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.2
5 2008/03/28 00:21:55 tgl
Exp $
PostgreSQL documentation
-->
...
...
@@ -36,7 +36,7 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ C
operation. This is most useful on large tables.
</para>
</refsect1>
<refsect1>
<title>Parameters</title>
...
...
@@ -91,8 +91,16 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ C
</para>
<para>
<command>TRUNCATE</> will not run any <literal>ON DELETE</literal>
triggers that might exist for the tables.
<command>TRUNCATE</> will not fire any <literal>ON DELETE</literal>
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>
<warning>
...
...
doc/src/sgml/trigger.sgml
View file @
7692d8d5
<!-- $PostgreSQL: pgsql/doc/src/sgml/trigger.sgml,v 1.5
1 2007/12/03 23:49:51
tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/trigger.sgml,v 1.5
2 2008/03/28 00:21:55
tgl Exp $ -->
<chapter id="triggers">
<title>Triggers</title>
...
...
@@ -36,14 +36,15 @@
performed. 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.
If a trigger event occurs, the trigger's function is called
at the appropriate time to handle the event.
or once per <acronym>SQL</acronym> 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.
</para>
<para>
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</>.
(The trigger function receives its input through a specially-passed
<structname>TriggerData</> structure, not in the form of ordinary function
...
...
@@ -69,7 +70,8 @@
in the execution of any applicable per-statement triggers. These
two types of triggers are sometimes called <firstterm>row-level</>
triggers and <firstterm>statement-level</> triggers,
respectively.
respectively. Triggers on <command>TRUNCATE</command> may only be
defined at statement-level.
</para>
<para>
...
...
@@ -398,6 +400,15 @@ typedef struct TriggerData
</para>
</listitem>
</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>
</para>
</listitem>
...
...
@@ -630,10 +641,10 @@ CREATE FUNCTION trigf() RETURNS trigger
AS '<replaceable>filename</>'
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();
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();
</programlisting>
</para>
...
...
src/backend/commands/tablecmds.c
View file @
7692d8d5
...
...
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.24
8 2008/03/27 03:57:33
tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.24
9 2008/03/28 00:21:55
tgl Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -539,6 +539,9 @@ ExecuteTruncate(TruncateStmt *stmt)
{
List
*
rels
=
NIL
;
List
*
relids
=
NIL
;
EState
*
estate
;
ResultRelInfo
*
resultRelInfos
;
ResultRelInfo
*
resultRelInfo
;
ListCell
*
cell
;
/*
...
...
@@ -601,6 +604,45 @@ ExecuteTruncate(TruncateStmt *stmt)
heap_truncate_check_FKs
(
rels
,
false
);
#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.
*/
...
...
@@ -637,6 +679,23 @@ ExecuteTruncate(TruncateStmt *stmt)
*/
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
);
}
/*
...
...
src/backend/commands/trigger.c
View file @
7692d8d5
...
...
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.23
0 2008/03/26 21:10:38 alvherre
Exp $
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.23
1 2008/03/28 00:21:55 tgl
Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -179,6 +179,18 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid)
errmsg
(
"multiple UPDATE events specified"
)));
TRIGGER_SETT_UPDATE
(
tgtype
);
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:
elog
(
ERROR
,
"unrecognized trigger event: %d"
,
(
int
)
stmt
->
actions
[
i
]);
...
...
@@ -1299,6 +1311,15 @@ InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx)
(
*
tp
)[
n
[
TRIGGER_EVENT_UPDATE
]]
=
indx
;
(
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,
}
}
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
GetTupleForTrigger
(
EState
*
estate
,
ResultRelInfo
*
relinfo
,
...
...
@@ -3571,6 +3661,12 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
if
(
afterTriggers
==
NULL
)
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
*/
...
...
src/backend/executor/execMain.c
View file @
7692d8d5
...
...
@@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.30
4 2008/03/26 21:10:38 alvherre
Exp $
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.30
5 2008/03/28 00:21:55 tgl
Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -66,11 +66,6 @@ typedef struct evalPlanQual
/* decls for local routines only used within this module */
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
TupleTableSlot
*
ExecutePlan
(
EState
*
estate
,
PlanState
*
planstate
,
CmdType
operation
,
...
...
@@ -525,7 +520,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
resultRelationOid
=
getrelid
(
resultRelationIndex
,
rangeTable
);
resultRelation
=
heap_open
(
resultRelationOid
,
RowExclusiveLock
);
i
nitResultRelInfo
(
resultRelInfo
,
I
nitResultRelInfo
(
resultRelInfo
,
resultRelation
,
resultRelationIndex
,
operation
,
...
...
@@ -860,8 +855,8 @@ InitPlan(QueryDesc *queryDesc, int eflags)
/*
* Initialize ResultRelInfo data for one result relation
*/
static
void
i
nitResultRelInfo
(
ResultRelInfo
*
resultRelInfo
,
void
I
nitResultRelInfo
(
ResultRelInfo
*
resultRelInfo
,
Relation
resultRelationDesc
,
Index
resultRelationIndex
,
CmdType
operation
,
...
...
@@ -997,11 +992,11 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
/*
* Make the new entry in the right context. Currently, we don't need any
* index information in ResultRelInfos used only for triggers, so tell
*
i
nitResultRelInfo it's a DELETE.
*
I
nitResultRelInfo it's a DELETE.
*/
oldcontext
=
MemoryContextSwitchTo
(
estate
->
es_query_cxt
);
rInfo
=
makeNode
(
ResultRelInfo
);
i
nitResultRelInfo
(
rInfo
,
I
nitResultRelInfo
(
rInfo
,
rel
,
0
,
/* dummy rangetable index */
CMD_DELETE
,
...
...
src/backend/parser/gram.y
View file @
7692d8d5
...
...
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.61
0 2008/03/21 22:41:48
tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.61
1 2008/03/28 00:21:55
tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
...
...
@@ -2719,6 +2719,7 @@ TriggerOneEvent:
INSERT { $$ = 'i'; }
| DELETE_P { $$ = 'd'; }
| UPDATE { $$ = 'u'; }
| TRUNCATE { $$ = 't'; }
;
TriggerForSpec:
...
...
src/backend/utils/adt/ruleutils.c
View file @
7692d8d5
...
...
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.27
1 2008/03/26 21:10:39 alvherre
Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.27
2 2008/03/28 00:21:56 tgl
Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -499,6 +499,13 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
else
appendStringInfo
(
&
buf
,
" UPDATE"
);
}
if
(
TRIGGER_FOR_TRUNCATE
(
trigrec
->
tgtype
))
{
if
(
findx
>
0
)
appendStringInfo
(
&
buf
,
" OR TRUNCATE"
);
else
appendStringInfo
(
&
buf
,
" TRUNCATE"
);
}
appendStringInfo
(
&
buf
,
" ON %s "
,
generate_relation_name
(
trigrec
->
tgrelid
));
...
...
src/bin/pg_dump/pg_dump.c
View file @
7692d8d5
...
...
@@ -12,7 +12,7 @@
* by PostgreSQL
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.48
5 2008/03/27 03:57:33
tgl Exp $
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.48
6 2008/03/28 00:21:56
tgl Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -9631,6 +9631,13 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
else
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
"
,
fmtId
(
tbinfo
->
dobj
.
name
));
...
...
src/include/catalog/pg_trigger.h
View file @
7692d8d5
...
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_trigger.h,v 1.3
1 2008/03/27 03:57:34
tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_trigger.h,v 1.3
2 2008/03/28 00:21:56
tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
...
...
@@ -89,6 +89,7 @@ typedef FormData_pg_trigger *Form_pg_trigger;
#define TRIGGER_TYPE_INSERT (1 << 2)
#define TRIGGER_TYPE_DELETE (1 << 3)
#define TRIGGER_TYPE_UPDATE (1 << 4)
#define TRIGGER_TYPE_TRUNCATE (1 << 5)
/* Macros for manipulating tgtype */
#define TRIGGER_CLEAR_TYPE(type) ((type) = 0)
...
...
@@ -98,11 +99,13 @@ typedef FormData_pg_trigger *Form_pg_trigger;
#define TRIGGER_SETT_INSERT(type) ((type) |= TRIGGER_TYPE_INSERT)
#define TRIGGER_SETT_DELETE(type) ((type) |= TRIGGER_TYPE_DELETE)
#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_BEFORE(type) ((type) & TRIGGER_TYPE_BEFORE)
#define TRIGGER_FOR_INSERT(type) ((type) & TRIGGER_TYPE_INSERT)
#define TRIGGER_FOR_DELETE(type) ((type) & TRIGGER_TYPE_DELETE)
#define TRIGGER_FOR_UPDATE(type) ((type) & TRIGGER_TYPE_UPDATE)
#define TRIGGER_FOR_TRUNCATE(type) ((type) & TRIGGER_TYPE_TRUNCATE)
#endif
/* PG_TRIGGER_H */
src/include/commands/trigger.h
View file @
7692d8d5
...
...
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.6
6 2008/01/02 23:34:42
tgl Exp $
* $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.6
7 2008/03/28 00:21:56
tgl Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -38,11 +38,18 @@ typedef struct TriggerData
Buffer
tg_newtuplebuf
;
}
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_DELETE 0x00000001
#define TRIGGER_EVENT_UPDATE 0x00000002
#define TRIGGER_EVENT_TRUNCATE 0x00000003
#define TRIGGER_EVENT_OPMASK 0x00000003
#define TRIGGER_EVENT_ROW 0x00000004
#define TRIGGER_EVENT_BEFORE 0x00000008
...
...
@@ -66,6 +73,10 @@ typedef struct TriggerData
(((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \
TRIGGER_EVENT_UPDATE)
#define TRIGGER_FIRED_BY_TRUNCATE(event) \
(((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \
TRIGGER_EVENT_TRUNCATE)
#define TRIGGER_FIRED_FOR_ROW(event) \
((TriggerEvent) (event) & TRIGGER_EVENT_ROW)
...
...
@@ -140,6 +151,10 @@ extern void ExecARUpdateTriggers(EState *estate,
ResultRelInfo
*
relinfo
,
ItemPointer
tupleid
,
HeapTuple
newtuple
);
extern
void
ExecBSTruncateTriggers
(
EState
*
estate
,
ResultRelInfo
*
relinfo
);
extern
void
ExecASTruncateTriggers
(
EState
*
estate
,
ResultRelInfo
*
relinfo
);
extern
void
AfterTriggerBeginXact
(
void
);
extern
void
AfterTriggerBeginQuery
(
void
);
...
...
src/include/executor/executor.h
View file @
7692d8d5
...
...
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.14
6 2008/01/01 19:45:57 momjian
Exp $
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.14
7 2008/03/28 00:21:56 tgl
Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -138,6 +138,11 @@ extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc,
ScanDirection
direction
,
long
count
);
extern
void
ExecutorEnd
(
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
bool
ExecContextForcesOids
(
PlanState
*
planstate
,
bool
*
hasoids
);
extern
void
ExecConstraints
(
ResultRelInfo
*
resultRelInfo
,
...
...
src/include/utils/rel.h
View file @
7692d8d5
...
...
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.10
4 2008/01/01 19:45:59 momjian
Exp $
* $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.10
5 2008/03/28 00:21:56 tgl
Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -71,9 +71,10 @@ typedef struct TriggerDesc
/*
* 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
* 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_row
[
TRIGGER_NUM_EVENT_CLASSES
];
...
...
src/pl/plperl/plperl.c
View file @
7692d8d5
/**********************************************************************
* plperl.c - perl as a procedural language for PostgreSQL
*
* $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.13
8 2008/03/25 22:42:45
tgl Exp $
* $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.13
9 2008/03/28 00:21:56
tgl Exp $
*
**********************************************************************/
...
...
@@ -689,6 +689,8 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo)
tupdesc
));
}
}
else
if
(
TRIGGER_FIRED_BY_TRUNCATE
(
tdata
->
tg_event
))
event
=
"TRUNCATE"
;
else
event
=
"UNKNOWN"
;
...
...
@@ -1395,6 +1397,8 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
retval
=
(
Datum
)
trigdata
->
tg_newtuple
;
else
if
(
TRIGGER_FIRED_BY_DELETE
(
trigdata
->
tg_event
))
retval
=
(
Datum
)
trigdata
->
tg_trigtuple
;
else
if
(
TRIGGER_FIRED_BY_TRUNCATE
(
trigdata
->
tg_event
))
retval
=
(
Datum
)
trigdata
->
tg_trigtuple
;
else
retval
=
(
Datum
)
0
;
/* can this happen? */
}
...
...
src/pl/plpgsql/src/pl_exec.c
View file @
7692d8d5
...
...
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.20
6 2008/03/26 18:48:59 alvherre
Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.20
7 2008/03/28 00:21:56 tgl
Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -538,8 +538,10 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
var
->
value
=
CStringGetTextDatum
(
"UPDATE"
);
else
if
(
TRIGGER_FIRED_BY_DELETE
(
trigdata
->
tg_event
))
var
->
value
=
CStringGetTextDatum
(
"DELETE"
);
else
if
(
TRIGGER_FIRED_BY_TRUNCATE
(
trigdata
->
tg_event
))
var
->
value
=
CStringGetTextDatum
(
"TRUNCATE"
);
else
elog
(
ERROR
,
"unrecognized trigger action: not INSERT, DELETE,
or UPD
ATE"
);
elog
(
ERROR
,
"unrecognized trigger action: not INSERT, DELETE,
UPDATE, or TRUNC
ATE"
);
var
->
isnull
=
false
;
var
->
freeval
=
true
;
...
...
src/pl/plpython/plpython.c
View file @
7692d8d5
/**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL
*
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.10
7 2008/03/25 22:42:45
tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.10
8 2008/03/28 00:21:56
tgl Exp $
*
*********************************************************************
*/
...
...
@@ -714,6 +714,8 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
pltevent
=
PyString_FromString
(
"DELETE"
);
else
if
(
TRIGGER_FIRED_BY_UPDATE
(
tdata
->
tg_event
))
pltevent
=
PyString_FromString
(
"UPDATE"
);
else
if
(
TRIGGER_FIRED_BY_TRUNCATE
(
tdata
->
tg_event
))
pltevent
=
PyString_FromString
(
"TRUNCATE"
);
else
{
elog
(
ERROR
,
"unrecognized OP tg_event: %u"
,
tdata
->
tg_event
);
...
...
src/pl/tcl/pltcl.c
View file @
7692d8d5
...
...
@@ -2,7 +2,7 @@
* pltcl.c - PostgreSQL support for Tcl as
* procedural language (PL)
*
* $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.11
8 2008/03/25 22:42:4
6 tgl Exp $
* $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.11
9 2008/03/28 00:21:5
6 tgl Exp $
*
**********************************************************************/
...
...
@@ -824,6 +824,8 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
Tcl_DStringAppendElement
(
&
tcl_cmd
,
"DELETE"
);
else
if
(
TRIGGER_FIRED_BY_UPDATE
(
trigdata
->
tg_event
))
Tcl_DStringAppendElement
(
&
tcl_cmd
,
"UPDATE"
);
else
if
(
TRIGGER_FIRED_BY_TRUNCATE
(
trigdata
->
tg_event
))
Tcl_DStringAppendElement
(
&
tcl_cmd
,
"TRUNCATE"
);
else
elog
(
ERROR
,
"unrecognized OP tg_event: %u"
,
trigdata
->
tg_event
);
...
...
src/test/regress/expected/truncate.out
View file @
7692d8d5
...
...
@@ -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_e_b_fkey on table trunc_e
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();
src/test/regress/sql/truncate.sql
View file @
7692d8d5
...
...
@@ -77,3 +77,56 @@ SELECT * FROM truncate_a
SELECT
*
FROM
trunc_e
;
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
();
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment