1919#include "catalog/pg_type.h"
2020#include "commands/event_trigger.h"
2121#include "commands/trigger.h"
22+ #include "executor/spi.h"
2223#include "funcapi.h"
2324#include "utils/builtins.h"
2425#include "utils/lsyscache.h"
@@ -29,6 +30,7 @@ PG_MODULE_MAGIC;
2930PG_FUNCTION_INFO_V1 (plsample_call_handler );
3031
3132static Datum plsample_func_handler (PG_FUNCTION_ARGS );
33+ static HeapTuple plsample_trigger_handler (PG_FUNCTION_ARGS );
3234
3335/*
3436 * Handle function, procedure, and trigger calls.
@@ -38,6 +40,11 @@ plsample_call_handler(PG_FUNCTION_ARGS)
3840{
3941 Datum retval = (Datum ) 0 ;
4042
43+ /*
44+ * Many languages will require cleanup that happens even in the event of
45+ * an error. That can happen in the PG_FINALLY block. If none is needed,
46+ * this PG_TRY construct can be omitted.
47+ */
4148 PG_TRY ();
4249 {
4350 /*
@@ -51,13 +58,16 @@ plsample_call_handler(PG_FUNCTION_ARGS)
5158 * (TriggerData *) fcinfo->context includes the information of the
5259 * context.
5360 */
61+ retval = PointerGetDatum (plsample_trigger_handler (fcinfo ));
5462 }
5563 else if (CALLED_AS_EVENT_TRIGGER (fcinfo ))
5664 {
5765 /*
5866 * This function is called as an event trigger function, where
5967 * (EventTriggerData *) fcinfo->context includes the information
6068 * of the context.
69+ *
70+ * TODO: provide an example handler.
6171 */
6272 }
6373 else
@@ -101,9 +111,9 @@ plsample_func_handler(PG_FUNCTION_ARGS)
101111 FmgrInfo result_in_func ;
102112 int numargs ;
103113
104- /* Fetch the source text of the function . */
105- pl_tuple = SearchSysCache (PROCOID ,
106- ObjectIdGetDatum (fcinfo -> flinfo -> fn_oid ), 0 , 0 , 0 );
114+ /* Fetch the function's pg_proc entry . */
115+ pl_tuple = SearchSysCache1 (PROCOID ,
116+ ObjectIdGetDatum (fcinfo -> flinfo -> fn_oid ));
107117 if (!HeapTupleIsValid (pl_tuple ))
108118 elog (ERROR , "cache lookup failed for function %u" ,
109119 fcinfo -> flinfo -> fn_oid );
@@ -185,3 +195,160 @@ plsample_func_handler(PG_FUNCTION_ARGS)
185195 ret = InputFunctionCall (& result_in_func , source , result_typioparam , -1 );
186196 PG_RETURN_DATUM (ret );
187197}
198+
199+ /*
200+ * plsample_trigger_handler
201+ *
202+ * Function called by the call handler for trigger execution.
203+ */
204+ static HeapTuple
205+ plsample_trigger_handler (PG_FUNCTION_ARGS )
206+ {
207+ TriggerData * trigdata = (TriggerData * ) fcinfo -> context ;
208+ char * string ;
209+ volatile HeapTuple rettup ;
210+ HeapTuple pl_tuple ;
211+ Datum ret ;
212+ char * source ;
213+ bool isnull ;
214+ Form_pg_proc pl_struct ;
215+ char * proname ;
216+ int rc PG_USED_FOR_ASSERTS_ONLY ;
217+
218+ /* Make sure this is being called from a trigger. */
219+ if (!CALLED_AS_TRIGGER (fcinfo ))
220+ elog (ERROR , "not called by trigger manager" );
221+
222+ /* Connect to the SPI manager */
223+ if (SPI_connect () != SPI_OK_CONNECT )
224+ elog (ERROR , "could not connect to SPI manager" );
225+
226+ rc = SPI_register_trigger_data (trigdata );
227+ Assert (rc >= 0 );
228+
229+ /* Fetch the function's pg_proc entry. */
230+ pl_tuple = SearchSysCache1 (PROCOID ,
231+ ObjectIdGetDatum (fcinfo -> flinfo -> fn_oid ));
232+ if (!HeapTupleIsValid (pl_tuple ))
233+ elog (ERROR , "cache lookup failed for function %u" ,
234+ fcinfo -> flinfo -> fn_oid );
235+
236+ /*
237+ * Code Retrieval
238+ *
239+ * Extract and print the source text of the function. This can be used as
240+ * a base for the function validation and execution.
241+ */
242+ pl_struct = (Form_pg_proc ) GETSTRUCT (pl_tuple );
243+ proname = pstrdup (NameStr (pl_struct -> proname ));
244+ ret = SysCacheGetAttr (PROCOID , pl_tuple , Anum_pg_proc_prosrc , & isnull );
245+ if (isnull )
246+ elog (ERROR , "could not find source text of function \"%s\"" ,
247+ proname );
248+ source = DatumGetCString (DirectFunctionCall1 (textout , ret ));
249+ ereport (NOTICE ,
250+ (errmsg ("source text of function \"%s\": %s" ,
251+ proname , source )));
252+
253+ /*
254+ * We're done with the pg_proc tuple, so release it. (Note that the
255+ * "proname" and "source" strings are now standalone copies.)
256+ */
257+ ReleaseSysCache (pl_tuple );
258+
259+ /*
260+ * Code Augmentation
261+ *
262+ * The source text may be augmented here, such as by wrapping it as the
263+ * body of a function in the target language, prefixing a parameter list
264+ * with names like TD_name, TD_relid, TD_table_name, TD_table_schema,
265+ * TD_event, TD_when, TD_level, TD_NEW, TD_OLD, and args, using whatever
266+ * types in the target language are convenient. The augmented text can be
267+ * cached in a longer-lived memory context, or, if the target language
268+ * uses a compilation step, that can be done here, caching the result of
269+ * the compilation.
270+ */
271+
272+ /*
273+ * Code Execution
274+ *
275+ * Here the function (the possibly-augmented source text, or the result of
276+ * compilation if the target language uses such a step) should be
277+ * executed, after binding values from the TriggerData struct to the
278+ * appropriate parameters.
279+ *
280+ * In this example we just print a lot of info via ereport.
281+ */
282+
283+ PG_TRY ();
284+ {
285+ ereport (NOTICE ,
286+ (errmsg ("trigger name: %s" , trigdata -> tg_trigger -> tgname )));
287+ string = SPI_getrelname (trigdata -> tg_relation );
288+ ereport (NOTICE , (errmsg ("trigger relation: %s" , string )));
289+
290+ string = SPI_getnspname (trigdata -> tg_relation );
291+ ereport (NOTICE , (errmsg ("trigger relation schema: %s" , string )));
292+
293+ /* Example handling of different trigger aspects. */
294+
295+ if (TRIGGER_FIRED_BY_INSERT (trigdata -> tg_event ))
296+ {
297+ ereport (NOTICE , (errmsg ("triggered by INSERT" )));
298+ rettup = trigdata -> tg_trigtuple ;
299+ }
300+ else if (TRIGGER_FIRED_BY_DELETE (trigdata -> tg_event ))
301+ {
302+ ereport (NOTICE , (errmsg ("triggered by DELETE" )));
303+ rettup = trigdata -> tg_trigtuple ;
304+ }
305+ else if (TRIGGER_FIRED_BY_UPDATE (trigdata -> tg_event ))
306+ {
307+ ereport (NOTICE , (errmsg ("triggered by UPDATE" )));
308+ rettup = trigdata -> tg_trigtuple ;
309+ }
310+ else if (TRIGGER_FIRED_BY_TRUNCATE (trigdata -> tg_event ))
311+ {
312+ ereport (NOTICE , (errmsg ("triggered by TRUNCATE" )));
313+ rettup = trigdata -> tg_trigtuple ;
314+ }
315+ else
316+ elog (ERROR , "unrecognized event: %u" , trigdata -> tg_event );
317+
318+ if (TRIGGER_FIRED_BEFORE (trigdata -> tg_event ))
319+ ereport (NOTICE , (errmsg ("triggered BEFORE" )));
320+ else if (TRIGGER_FIRED_AFTER (trigdata -> tg_event ))
321+ ereport (NOTICE , (errmsg ("triggered AFTER" )));
322+ else if (TRIGGER_FIRED_INSTEAD (trigdata -> tg_event ))
323+ ereport (NOTICE , (errmsg ("triggered INSTEAD OF" )));
324+ else
325+ elog (ERROR , "unrecognized when: %u" , trigdata -> tg_event );
326+
327+ if (TRIGGER_FIRED_FOR_ROW (trigdata -> tg_event ))
328+ ereport (NOTICE , (errmsg ("triggered per row" )));
329+ else if (TRIGGER_FIRED_FOR_STATEMENT (trigdata -> tg_event ))
330+ ereport (NOTICE , (errmsg ("triggered per statement" )));
331+ else
332+ elog (ERROR , "unrecognized level: %u" , trigdata -> tg_event );
333+
334+ /*
335+ * Iterate through all of the trigger arguments, printing each input
336+ * value.
337+ */
338+ for (int i = 0 ; i < trigdata -> tg_trigger -> tgnargs ; i ++ )
339+ ereport (NOTICE ,
340+ (errmsg ("trigger arg[%i]: %s" , i ,
341+ trigdata -> tg_trigger -> tgargs [i ])));
342+ }
343+ PG_CATCH ();
344+ {
345+ /* Error cleanup code would go here */
346+ PG_RE_THROW ();
347+ }
348+ PG_END_TRY ();
349+
350+ if (SPI_finish () != SPI_OK_FINISH )
351+ elog (ERROR , "SPI_finish() failed" );
352+
353+ return rettup ;
354+ }
0 commit comments