From 18952f67446da73f938d213b5225b99e95657837 Mon Sep 17 00:00:00 2001
From: Tom Lane
Date: Mon, 29 May 2000 01:59:17 +0000
Subject: Second round of fmgr changes: triggers are now invoked in new style,
CurrentTriggerData is history.
---
doc/src/sgml/ref/create_language.sgml | 102 +++++++++++++++++++---------------
doc/src/sgml/trigger.sgml | 82 +++++++++++++++++++--------
2 files changed, 117 insertions(+), 67 deletions(-)
(limited to 'doc/src/sgml')
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 @@
@@ -160,44 +160,42 @@ ERROR: PL handler function funcname
Writing PL handlers
+
+
+
+ In Postgres 7.1 and later, call handlers
+ must adhere to the "new style" function manager interface.
+
+
+
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
Postgres as a function taking
no arguments and returning the
- opaque type, a placeholder for unspecified or undefined types..
+ opaque 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.)
+
- 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.
-
-
-
- When called from the trigger manager, the only argument is
- the object ID from the procedure's pg_proc
- entry. All other
- information from the trigger manager is found in the
- global CurrentTriggerData pointer.
-
-
-
-
- When called from the function manager, the arguments are
- the object ID of the procedure's pg_proc
- entry, the number
- of arguments given to the PL function, the arguments in a
- FmgrValues structure and a pointer
- to a boolean where the
- function tells the caller if the return value is the SQL
- NULL value.
-
-
-
+ 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
+ isnull 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
+ flinfo->fn_oid 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.
+
It's up to the call handler to fetch the
pg_proc entry and
@@ -212,6 +210,28 @@ ERROR: PL handler function funcname
+
+
+ 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 flinfo->fn_extra 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 flinfo->fn_extra is already non-NULL then it
+ can be used and the information lookup step skipped. The call handler
+ must be careful that flinfo->fn_extra 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.
+
+
+
+ When a PL function is invoked as a trigger, no explicit arguments
+ are passed, but the FunctionCallInfoData's
+ context 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.
+
@@ -275,39 +295,33 @@ ERROR: PL handler function funcnamecontext;
retval = ...
} else {
/*
- * Called as a trigger procedure
+ * Called as a function
*/
- trigdata = CurrentTriggerData;
- CurrentTriggerData = NULL;
retval = ...
}
- *isNull = false;
return retval;
}
@@ -325,7 +339,7 @@ plsample_call_handler(
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 @@
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.
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.
@@ -106,7 +107,7 @@ CREATE TRIGGER trigger [ BEFORE | AFTER ] [ INSERT |
args
- 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.
@@ -179,11 +180,35 @@ CREATE TRIGGER trigger [ BEFORE | AFTER ] [ INSERT |
Interaction with the Trigger Manager
- 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.
+
+
+
+
+ The interface described here applies for
+ Postgres 7.1 and later.
+ Earlier versions passed the TriggerData pointer in a global
+ variable CurrentTriggerData.
+
+
+
+
+ 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
+ CALLED_AS_TRIGGER(fcinfo), which expands to
+
+ ((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
+
+ If this returns TRUE, then it is safe to cast fcinfo->context to type
+ TriggerData * and make use of the pointed-to
+ TriggerData structure.
+ The function must not alter the TriggerData
+ structure or any of the data it points to.
@@ -192,6 +217,7 @@ CREATE TRIGGER trigger [ BEFORE | AFTER ] [ INSERT |
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:
+
+ type
+
+
+ Always T_TriggerData if this is a trigger event.
+
+
+
+
tg_event
@@ -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);
}
--
cgit v1.2.3