📄 trigger.sgml
字号:
</listitem> </varlistentry> </variablelist> </para> </listitem> </varlistentry> <varlistentry> <term><structfield>tg_relation</></term> <listitem> <para> A pointer to a structure describing the relation that the trigger fired for. Look at <filename>utils/rel.h</> for details about this structure. The most interesting things are <literal>tg_relation->rd_att</> (descriptor of the relation tuples) and <literal>tg_relation->rd_rel->relname</> (relation name; the type is not <type>char*</> but <type>NameData</>; use <literal>SPI_getrelname(tg_relation)</> to get a <type>char*</> if you need a copy of the name). </para> </listitem> </varlistentry> <varlistentry> <term><structfield>tg_trigtuple</></term> <listitem> <para> A pointer to the row for which the trigger was fired. This is the row being inserted, updated, or deleted. If this trigger was fired for an <command>INSERT</command> or <command>DELETE</command> then this is what you should return to from the function if you don't want to replace the row with a different one (in the case of <command>INSERT</command>) or skip the operation. </para> </listitem> </varlistentry> <varlistentry> <term><structfield>tg_newtuple</></term> <listitem> <para> A pointer to the new version of the row, if the trigger was fired for an <command>UPDATE</command>, and <symbol>NULL</> if it is for an <command>INSERT</command> or a <command>DELETE</command>. This is what you have to return from the function if the event is an <command>UPDATE</command> and you don't want to replace this row by a different one or skip the operation. </para> </listitem> </varlistentry> <varlistentry> <term><structfield>tg_trigger</></term> <listitem> <para> A pointer to a structure of type <structname>Trigger</>, defined in <filename>utils/rel.h</>:<programlisting>typedef struct Trigger{ Oid tgoid; char *tgname; Oid tgfoid; int16 tgtype; bool tgenabled; bool tgisconstraint; Oid tgconstrrelid; bool tgdeferrable; bool tginitdeferred; int16 tgnargs; int16 tgattr[FUNC_MAX_ARGS]; char **tgargs;} Trigger;</programlisting> where <structfield>tgname</> is the trigger's name, <structfield>tgnargs</> is number of arguments in <structfield>tgargs</>, and <structfield>tgargs</> is an array of pointers to the arguments specified in the <command>CREATE TRIGGER</command> statement. The other members are for internal use only. </para> </listitem> </varlistentry> </variablelist> </para> <para> A trigger function must return either <symbol>NULL</> or a <structname>HeapTuple</> pointer. Be careful to return either <structfield>tg_trigtuple</> or <structfield>tg_newtuple</>, as appropriate, if you don't want to modify the row being operated on. </para> </sect1> <sect1 id="trigger-example"> <title>A Complete Example</title> <para> Here is a very simple example of a trigger function written in C. (Examples of triggers written in procedural languages may be found in the documentation of the procedural languages.) </para> <para> The function <function>trigf</> reports the number of rows in the table <literal>ttest</> and skips the actual operation if the command attempts to insert a null value into the column <literal>x</>. (So the trigger acts as a not-null constraint but doesn't abort the transaction.) </para> <para> First, the table definition:<programlisting>CREATE TABLE ttest ( x integer);</programlisting> </para> <para> This is the source code of the trigger function:<programlisting>#include "postgres.h"#include "executor/spi.h" /* this is what you need to work with SPI */#include "commands/trigger.h" /* ... and triggers */extern Datum trigf(PG_FUNCTION_ARGS);PG_FUNCTION_INFO_V1(trigf);Datumtrigf(PG_FUNCTION_ARGS){ TriggerData *trigdata = (TriggerData *) fcinfo->context; TupleDesc tupdesc; HeapTuple rettuple; char *when; bool checknull = false; bool isnull; int ret, i; /* make sure it's called as a trigger at all */ if (!CALLED_AS_TRIGGER(fcinfo)) elog(ERROR, "trigf: not called by trigger manager"); /* tuple to return to executor */ if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) rettuple = trigdata->tg_newtuple; else rettuple = trigdata->tg_trigtuple; /* check for null values */ if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event) && TRIGGER_FIRED_BEFORE(trigdata->tg_event)) checknull = true; if (TRIGGER_FIRED_BEFORE(trigdata->tg_event)) when = "before"; else when = "after "; tupdesc = trigdata->tg_relation->rd_att; /* connect to SPI manager */ if ((ret = SPI_connect()) < 0) elog(INFO, "trigf (fired %s): SPI_connect returned %d", when, ret); /* get number of rows in table */ ret = SPI_exec("SELECT count(*) FROM ttest", 0); if (ret < 0) elog(NOTICE, "trigf (fired %s): SPI_exec returned %d", when, ret); /* count(*) returns int8, so be careful to convert */ i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); elog (INFO, "trigf (fired %s): there are %d rows in ttest", when, i); SPI_finish(); if (checknull) { SPI_getbinval(rettuple, tupdesc, 1, &isnull); if (isnull) rettuple = NULL; } return PointerGetDatum(rettuple);}</programlisting> </para> <para> After you have compiled the source code, declare the function and the triggers:<programlisting>CREATE FUNCTION trigf() RETURNS trigger AS '<replaceable>filename</>' LANGUAGE C;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 FOR EACH ROW EXECUTE PROCEDURE trigf();</programlisting> </para> <para> Now you can test the operation of the trigger:<screen>=> INSERT INTO ttest VALUES (NULL);INFO: trigf (fired before): there are 0 rows in ttestINSERT 0 0-- Insertion skipped and AFTER trigger is not fired=> SELECT * FROM ttest; x---(0 rows)=> INSERT INTO ttest VALUES (1);INFO: trigf (fired before): there are 0 rows in ttestINFO: trigf (fired after ): there are 1 rows in ttest ^^^^^^^^ remember what we said about visibility.INSERT 167793 1vac=> SELECT * FROM ttest; x--- 1(1 row)=> INSERT INTO ttest SELECT x * 2 FROM ttest;INFO: trigf (fired before): there are 1 rows in ttestINFO: trigf (fired after ): there are 2 rows in ttest ^^^^^^ remember what we said about visibility.INSERT 167794 1=> SELECT * FROM ttest; x--- 1 2(2 rows)=> UPDATE ttest SET x = NULL WHERE x = 2;INFO: trigf (fired before): there are 2 rows in ttestUPDATE 0=> UPDATE ttest SET x = 4 WHERE x = 2;INFO: trigf (fired before): there are 2 rows in ttestINFO: trigf (fired after ): there are 2 rows in ttestUPDATE 1vac=> SELECT * FROM ttest; x--- 1 4(2 rows)=> DELETE FROM ttest;INFO: trigf (fired before): there are 2 rows in ttestINFO: trigf (fired after ): there are 1 rows in ttestINFO: trigf (fired before): there are 1 rows in ttestINFO: trigf (fired after ): there are 0 rows in ttest ^^^^^^ remember what we said about visibility.DELETE 2=> SELECT * FROM ttest; x---(0 rows)</screen> </para> <para> There are more complex examples in <filename>src/test/regress/regress.c</filename> and in <filename>contrib/spi</filename>. </para> </sect1> </chapter><!-- Keep this comment at the end of the fileLocal variables:mode:sgmlsgml-omittag:nilsgml-shorttag:tsgml-minimize-attributes:nilsgml-always-quote-attributes:tsgml-indent-step:1sgml-indent-data:tsgml-parent-document:nilsgml-default-dtd-file:"./reference.ced"sgml-exposed-tags:nilsgml-local-catalogs:("/usr/lib/sgml/catalog")sgml-local-ecat-files:nilEnd:-->
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -