diff options
author | Tom Lane | 2000-05-29 01:59:17 +0000 |
---|---|---|
committer | Tom Lane | 2000-05-29 01:59:17 +0000 |
commit | 18952f67446da73f938d213b5225b99e95657837 (patch) | |
tree | 7ad26843000b8a17e798dafacbd29baf2d390fa1 /doc/src | |
parent | 147ccf5c8045c79a9c8194044359e140c9074ed7 (diff) |
Second round of fmgr changes: triggers are now invoked in new style,
CurrentTriggerData is history.
Diffstat (limited to 'doc/src')
-rw-r--r-- | doc/src/sgml/ref/create_language.sgml | 102 | ||||
-rw-r--r-- | doc/src/sgml/trigger.sgml | 82 |
2 files changed, 117 insertions, 67 deletions
diff --git a/doc/src/sgml/ref/create_language.sgml b/doc/src/sgml/ref/create_language.sgml index 580490b3570..e8e84a6239a 100644 --- a/doc/src/sgml/ref/create_language.sgml +++ b/doc/src/sgml/ref/create_language.sgml @@ -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 function + * Called as a trigger procedure */ + TriggerData *trigdata = (TriggerData *) fcinfo->context; retval = ... } else { /* - * Called as a trigger procedure + * Called as a function */ - 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'; diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml index aa476a61a5c..48a4487e465 100644 --- a/doc/src/sgml/trigger.sgml +++ b/doc/src/sgml/trigger.sgml @@ -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; @@ -204,6 +230,15 @@ typedef struct TriggerData <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> <para> @@ -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> |