mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Second round of fmgr changes: triggers are now invoked in new style,
CurrentTriggerData is history.
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_language.sgml,v 1.9 2000/03/26 18:32:27 petere Exp $
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_language.sgml,v 1.10 2000/05/29 01:59:06 tgl Exp $
|
||||
Postgres documentation
|
||||
-->
|
||||
|
||||
@ -160,44 +160,42 @@ ERROR: PL handler function <replaceable class="parameter">funcname</replaceable
|
||||
<title>
|
||||
Writing PL handlers
|
||||
</title>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
In <productname>Postgres</productname> 7.1 and later, call handlers
|
||||
must adhere to the "new style" function manager interface.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
<para>
|
||||
The call handler for a procedural language must be written
|
||||
in a compiler language such as 'C' and registered with
|
||||
in a compiled language such as 'C' and registered with
|
||||
<productname>Postgres</productname> as a function taking
|
||||
no arguments and returning the
|
||||
<type>opaque</type> type, a placeholder for unspecified or undefined types..
|
||||
<type>opaque</type> type, a placeholder for unspecified or undefined types.
|
||||
This prevents the call handler from being
|
||||
called directly as a function from queries.
|
||||
(However, arguments may be supplied in the actual call when a
|
||||
PL function in the language offered by the handler is to be executed.)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
However, arguments must be supplied on the actual call when a
|
||||
PL function or trigger
|
||||
procedure in the language offered by the handler is to be
|
||||
executed.
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
When called from the trigger manager, the only argument is
|
||||
the object ID from the procedure's <filename>pg_proc</filename>
|
||||
entry. All other
|
||||
information from the trigger manager is found in the
|
||||
global <structname>CurrentTriggerData</structname> pointer.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
When called from the function manager, the arguments are
|
||||
the object ID of the procedure's <filename>pg_proc</filename>
|
||||
entry, the number
|
||||
of arguments given to the PL function, the arguments in a
|
||||
<structname>FmgrValues</structname> structure and a pointer
|
||||
to a boolean where the
|
||||
function tells the caller if the return value is the SQL
|
||||
NULL value.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
The call handler is called in the same way as any other new-style
|
||||
function: it receives a pointer to a FunctionCallInfoData struct
|
||||
containing argument values and information about the called function,
|
||||
and it is expected to return a Datum result (and possibly set the
|
||||
<literal>isnull</literal> field of the FunctionCallInfoData struct,
|
||||
if it wishes to return an SQL NULL result). The difference between
|
||||
a call handler and an ordinary callee function is that the
|
||||
<literal>flinfo->fn_oid</literal> field of the FunctionCallInfoData
|
||||
struct will contain the OID of the PL function to be called, not of
|
||||
the call handler itself. The call handler must use this field to
|
||||
determine which function to execute. Also, the passed argument list
|
||||
has been set up according to the declaration of the target PL function,
|
||||
not of the call handler.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
It's up to the call handler to fetch the
|
||||
<filename>pg_proc</filename> entry and
|
||||
@ -212,6 +210,28 @@ ERROR: PL handler function <replaceable class="parameter">funcname</replaceable
|
||||
file or anything else that tells the call handler what to
|
||||
do in detail.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Often, the same function is called many times per SQL statement.
|
||||
A call handler can avoid repeated lookups of information about the
|
||||
called function by using the <literal>flinfo->fn_extra</literal> field.
|
||||
This will initially be NULL, but can be set by the call handler to
|
||||
point at information about the PL function. On subsequent calls,
|
||||
if <literal>flinfo->fn_extra</literal> is already non-NULL then it
|
||||
can be used and the information lookup step skipped. The call handler
|
||||
must be careful that <literal>flinfo->fn_extra</literal> is made to
|
||||
point at memory that will live at least until the end of the current
|
||||
query, since an FmgrInfo data structure could be kept that long.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When a PL function is invoked as a trigger, no explicit arguments
|
||||
are passed, but the FunctionCallInfoData's
|
||||
<literal>context</literal> field points at a TriggerData node,
|
||||
rather than being NULL as it is in a plain function call.
|
||||
A PL handler should provide mechanisms for PL functions to get
|
||||
at the trigger information.
|
||||
</para>
|
||||
</refsect2>
|
||||
|
||||
<refsect2 id="R2-SQL-CREATELANGUAGE-4">
|
||||
@ -275,39 +295,33 @@ ERROR: PL handler function <replaceable class="parameter">funcname</replaceable
|
||||
#include "executor/spi.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "utils/elog.h"
|
||||
#include "fmgr.h" /* for FmgrValues struct */
|
||||
#include "fmgr.h"
|
||||
#include "access/heapam.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
|
||||
Datum
|
||||
plsample_call_handler(
|
||||
Oid prooid,
|
||||
int pronargs,
|
||||
FmgrValues *proargs,
|
||||
bool *isNull)
|
||||
plsample_call_handler(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum retval;
|
||||
TriggerData *trigdata;
|
||||
|
||||
if (CurrentTriggerData == NULL) {
|
||||
if (CALLED_AS_TRIGGER(fcinfo))
|
||||
{
|
||||
/*
|
||||
* Called as a trigger procedure
|
||||
*/
|
||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||
|
||||
retval = ...
|
||||
} else {
|
||||
/*
|
||||
* Called as a function
|
||||
*/
|
||||
|
||||
retval = ...
|
||||
} else {
|
||||
/*
|
||||
* Called as a trigger procedure
|
||||
*/
|
||||
trigdata = CurrentTriggerData;
|
||||
CurrentTriggerData = NULL;
|
||||
|
||||
retval = ...
|
||||
}
|
||||
|
||||
*isNull = false;
|
||||
return retval;
|
||||
}
|
||||
</programlisting>
|
||||
@ -325,7 +339,7 @@ plsample_call_handler(
|
||||
<programlisting>
|
||||
CREATE FUNCTION plsample_call_handler () RETURNS opaque
|
||||
AS '/usr/local/pgsql/lib/plsample.so'
|
||||
LANGUAGE 'C';
|
||||
LANGUAGE 'newC';
|
||||
CREATE PROCEDURAL LANGUAGE 'plsample'
|
||||
HANDLER plsample_call_handler
|
||||
LANCOMPILER 'PL/Sample';
|
||||
|
@ -15,13 +15,14 @@
|
||||
|
||||
<para>
|
||||
If a trigger event occurs, the trigger manager (called by the Executor)
|
||||
initializes the global structure TriggerData *CurrentTriggerData (described
|
||||
below) and calls the trigger function to handle the event.
|
||||
sets up a TriggerData information structure (described below) and calls
|
||||
the trigger function to handle the event.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The trigger function must be created before the trigger is created as a
|
||||
function taking no arguments and returns opaque.
|
||||
function taking no arguments and returning opaque. If the function is
|
||||
written in C, it must follow the "new style" function manager interface.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -106,7 +107,7 @@ CREATE TRIGGER <replaceable>trigger</replaceable> [ BEFORE | AFTER ] [ INSERT |
|
||||
<term><replaceable>args</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
The arguments passed to the function in the CurrentTriggerData structure.
|
||||
The arguments passed to the function in the TriggerData structure.
|
||||
The purpose of passing arguments to the function is to allow different
|
||||
triggers with similar requirements to call the same function.
|
||||
</para>
|
||||
@ -179,11 +180,35 @@ CREATE TRIGGER <replaceable>trigger</replaceable> [ BEFORE | AFTER ] [ INSERT |
|
||||
<title>Interaction with the Trigger Manager</title>
|
||||
|
||||
<para>
|
||||
As mentioned above, when function is called by the trigger manager,
|
||||
structure TriggerData *CurrentTriggerData is NOT NULL and initialized. So
|
||||
it is better to check CurrentTriggerData against being NULL at the start
|
||||
and set it to NULL just after fetching the information to prevent calls to
|
||||
a trigger function not from the trigger manager.
|
||||
This section describes the low-level details of the interface to a
|
||||
trigger function. This information is only needed when writing a
|
||||
trigger function in C. If you are using a higher-level function
|
||||
language then these details are handled for you.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
The interface described here applies for
|
||||
<productname>Postgres</productname> 7.1 and later.
|
||||
Earlier versions passed the TriggerData pointer in a global
|
||||
variable CurrentTriggerData.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
<para>
|
||||
When a function is called by the trigger manager, it is not passed any
|
||||
normal parameters, but it is passed a "context" pointer pointing to a
|
||||
TriggerData structure. C functions can check whether they were called
|
||||
from the trigger manager or not by executing the macro
|
||||
<literal>CALLED_AS_TRIGGER(fcinfo)</literal>, which expands to
|
||||
<programlisting>
|
||||
((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
|
||||
</programlisting>
|
||||
If this returns TRUE, then it is safe to cast fcinfo->context to type
|
||||
<literal>TriggerData *</literal> and make use of the pointed-to
|
||||
TriggerData structure.
|
||||
The function must <emphasis>not</emphasis> alter the TriggerData
|
||||
structure or any of the data it points to.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -192,6 +217,7 @@ CREATE TRIGGER <replaceable>trigger</replaceable> [ BEFORE | AFTER ] [ INSERT |
|
||||
<programlisting>
|
||||
typedef struct TriggerData
|
||||
{
|
||||
NodeTag type;
|
||||
TriggerEvent tg_event;
|
||||
Relation tg_relation;
|
||||
HeapTuple tg_trigtuple;
|
||||
@ -203,6 +229,15 @@ typedef struct TriggerData
|
||||
where the members are defined as follows:
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>type</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Always <literal>T_TriggerData</literal> if this is a trigger event.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>tg_event</term>
|
||||
<listitem>
|
||||
@ -410,11 +445,12 @@ execution of Q) or after Q is done.
|
||||
#include "executor/spi.h" /* this is what you need to work with SPI */
|
||||
#include "commands/trigger.h" /* -"- and triggers */
|
||||
|
||||
HeapTuple trigf(void);
|
||||
extern Datum trigf(PG_FUNCTION_ARGS);
|
||||
|
||||
HeapTuple
|
||||
trigf()
|
||||
Datum
|
||||
trigf(PG_FUNCTION_ARGS)
|
||||
{
|
||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||
TupleDesc tupdesc;
|
||||
HeapTuple rettuple;
|
||||
char *when;
|
||||
@ -422,27 +458,27 @@ trigf()
|
||||
bool isnull;
|
||||
int ret, i;
|
||||
|
||||
if (!CurrentTriggerData)
|
||||
elog(NOTICE, "trigf: triggers are not initialized");
|
||||
/* Make sure trigdata is pointing at what I expect */
|
||||
if (!CALLED_AS_TRIGGER(fcinfo))
|
||||
elog(ERROR, "trigf: not fired by trigger manager");
|
||||
|
||||
/* tuple to return to Executor */
|
||||
if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
|
||||
rettuple = CurrentTriggerData->tg_newtuple;
|
||||
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||
rettuple = trigdata->tg_newtuple;
|
||||
else
|
||||
rettuple = CurrentTriggerData->tg_trigtuple;
|
||||
rettuple = trigdata->tg_trigtuple;
|
||||
|
||||
/* check for NULLs ? */
|
||||
if (!TRIGGER_FIRED_BY_DELETE(CurrentTriggerData->tg_event) &&
|
||||
TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event))
|
||||
if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event) &&
|
||||
TRIGGER_FIRED_BEFORE(trigdata->tg_event))
|
||||
checknull = true;
|
||||
|
||||
if (TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event))
|
||||
if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
|
||||
when = "before";
|
||||
else
|
||||
when = "after ";
|
||||
|
||||
tupdesc = CurrentTriggerData->tg_relation->rd_att;
|
||||
CurrentTriggerData = NULL;
|
||||
tupdesc = trigdata->tg_relation->rd_att;
|
||||
|
||||
/* Connect to SPI manager */
|
||||
if ((ret = SPI_connect()) < 0)
|
||||
@ -467,7 +503,7 @@ trigf()
|
||||
rettuple = NULL;
|
||||
}
|
||||
|
||||
return (rettuple);
|
||||
return PointerGetDatum(rettuple);
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
|
Reference in New Issue
Block a user