19
19
#include "catalog/pg_type.h"
20
20
#include "commands/event_trigger.h"
21
21
#include "commands/trigger.h"
22
+ #include "executor/spi.h"
22
23
#include "funcapi.h"
23
24
#include "utils/builtins.h"
24
25
#include "utils/lsyscache.h"
@@ -29,6 +30,7 @@ PG_MODULE_MAGIC;
29
30
PG_FUNCTION_INFO_V1 (plsample_call_handler );
30
31
31
32
static Datum plsample_func_handler (PG_FUNCTION_ARGS );
33
+ static HeapTuple plsample_trigger_handler (PG_FUNCTION_ARGS );
32
34
33
35
/*
34
36
* Handle function, procedure, and trigger calls.
@@ -38,6 +40,11 @@ plsample_call_handler(PG_FUNCTION_ARGS)
38
40
{
39
41
Datum retval = (Datum ) 0 ;
40
42
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
+ */
41
48
PG_TRY ();
42
49
{
43
50
/*
@@ -51,13 +58,16 @@ plsample_call_handler(PG_FUNCTION_ARGS)
51
58
* (TriggerData *) fcinfo->context includes the information of the
52
59
* context.
53
60
*/
61
+ retval = PointerGetDatum (plsample_trigger_handler (fcinfo ));
54
62
}
55
63
else if (CALLED_AS_EVENT_TRIGGER (fcinfo ))
56
64
{
57
65
/*
58
66
* This function is called as an event trigger function, where
59
67
* (EventTriggerData *) fcinfo->context includes the information
60
68
* of the context.
69
+ *
70
+ * TODO: provide an example handler.
61
71
*/
62
72
}
63
73
else
@@ -101,9 +111,9 @@ plsample_func_handler(PG_FUNCTION_ARGS)
101
111
FmgrInfo result_in_func ;
102
112
int numargs ;
103
113
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 ));
107
117
if (!HeapTupleIsValid (pl_tuple ))
108
118
elog (ERROR , "cache lookup failed for function %u" ,
109
119
fcinfo -> flinfo -> fn_oid );
@@ -185,3 +195,160 @@ plsample_func_handler(PG_FUNCTION_ARGS)
185
195
ret = InputFunctionCall (& result_in_func , source , result_typioparam , -1 );
186
196
PG_RETURN_DATUM (ret );
187
197
}
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