Skip to content

Commit e5dc4cc

Browse files
committed
PL/Perl: Add event trigger support
From: Dimitri Fontaine <dimitri@2ndQuadrant.fr>
1 parent 6bea96d commit e5dc4cc

File tree

4 files changed

+242
-11
lines changed

4 files changed

+242
-11
lines changed

doc/src/sgml/plperl.sgml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,56 @@ CREATE TRIGGER test_valid_id_trig
12111211
</para>
12121212
</sect1>
12131213

1214+
<sect1 id="plperl-event-triggers">
1215+
<title>PL/Perl Event Triggers</title>
1216+
1217+
<para>
1218+
PL/Perl can be used to write event trigger functions. In an event trigger
1219+
function, the hash reference <varname>$_TD</varname> contains information
1220+
about the current trigger event. <varname>$_TD</> is a global variable,
1221+
which gets a separate local value for each invocation of the trigger. The
1222+
fields of the <varname>$_TD</varname> hash reference are:
1223+
1224+
<variablelist>
1225+
<varlistentry>
1226+
<term><literal>$_TD-&gt;{event}</literal></term>
1227+
<listitem>
1228+
<para>
1229+
The name of the event the trigger is fired for.
1230+
</para>
1231+
</listitem>
1232+
</varlistentry>
1233+
1234+
<varlistentry>
1235+
<term><literal>$_TD-&gt;{tag}</literal></term>
1236+
<listitem>
1237+
<para>
1238+
The command tag for which the trigger is fired.
1239+
</para>
1240+
</listitem>
1241+
</varlistentry>
1242+
</variablelist>
1243+
</para>
1244+
1245+
<para>
1246+
The return value of the trigger procedure is ignored.
1247+
</para>
1248+
1249+
<para>
1250+
Here is an example of an event trigger function, illustrating some of the
1251+
above:
1252+
<programlisting>
1253+
CREATE OR REPLACE FUNCTION perlsnitch() RETURNS event_trigger AS $$
1254+
elog(NOTICE, "perlsnitch: " . $_TD->{event} . " " . $_TD->{tag} . " ");
1255+
$$ LANGUAGE plperl;
1256+
1257+
CREATE EVENT TRIGGER perl_a_snitch
1258+
ON ddl_command_start
1259+
EXECUTE PROCEDURE perlsnitch();
1260+
</programlisting>
1261+
</para>
1262+
</sect1>
1263+
12141264
<sect1 id="plperl-under-the-hood">
12151265
<title>PL/Perl Under the Hood</title>
12161266

src/pl/plperl/expected/plperl_trigger.out

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,3 +309,38 @@ $$ LANGUAGE plperl;
309309
SELECT direct_trigger();
310310
ERROR: trigger functions can only be called as triggers
311311
CONTEXT: compilation of PL/Perl function "direct_trigger"
312+
-- test plperl command triggers
313+
create or replace function perlsnitch() returns event_trigger language plperl as $$
314+
elog(NOTICE, "perlsnitch: " . $_TD->{event} . " " . $_TD->{tag} . " ");
315+
$$;
316+
create event trigger perl_a_snitch on ddl_command_start
317+
execute procedure perlsnitch();
318+
create event trigger perl_b_snitch on ddl_command_end
319+
execute procedure perlsnitch();
320+
create or replace function foobar() returns int language sql as $$select 1;$$;
321+
NOTICE: perlsnitch: ddl_command_start CREATE FUNCTION
322+
CONTEXT: PL/Perl function "perlsnitch"
323+
NOTICE: perlsnitch: ddl_command_end CREATE FUNCTION
324+
CONTEXT: PL/Perl function "perlsnitch"
325+
alter function foobar() cost 77;
326+
NOTICE: perlsnitch: ddl_command_start ALTER FUNCTION
327+
CONTEXT: PL/Perl function "perlsnitch"
328+
NOTICE: perlsnitch: ddl_command_end ALTER FUNCTION
329+
CONTEXT: PL/Perl function "perlsnitch"
330+
drop function foobar();
331+
NOTICE: perlsnitch: ddl_command_start DROP FUNCTION
332+
CONTEXT: PL/Perl function "perlsnitch"
333+
NOTICE: perlsnitch: ddl_command_end DROP FUNCTION
334+
CONTEXT: PL/Perl function "perlsnitch"
335+
create table foo();
336+
NOTICE: perlsnitch: ddl_command_start CREATE TABLE
337+
CONTEXT: PL/Perl function "perlsnitch"
338+
NOTICE: perlsnitch: ddl_command_end CREATE TABLE
339+
CONTEXT: PL/Perl function "perlsnitch"
340+
drop table foo;
341+
NOTICE: perlsnitch: ddl_command_start DROP TABLE
342+
CONTEXT: PL/Perl function "perlsnitch"
343+
NOTICE: perlsnitch: ddl_command_end DROP TABLE
344+
CONTEXT: PL/Perl function "perlsnitch"
345+
drop event trigger perl_a_snitch;
346+
drop event trigger perl_b_snitch;

src/pl/plperl/plperl.c

Lines changed: 137 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "catalog/pg_language.h"
2222
#include "catalog/pg_proc.h"
2323
#include "catalog/pg_type.h"
24+
#include "commands/event_trigger.h"
2425
#include "commands/trigger.h"
2526
#include "executor/spi.h"
2627
#include "funcapi.h"
@@ -254,10 +255,13 @@ static void set_interp_require(bool trusted);
254255

255256
static Datum plperl_func_handler(PG_FUNCTION_ARGS);
256257
static Datum plperl_trigger_handler(PG_FUNCTION_ARGS);
258+
static void plperl_event_trigger_handler(PG_FUNCTION_ARGS);
257259

258260
static void free_plperl_function(plperl_proc_desc *prodesc);
259261

260-
static plperl_proc_desc *compile_plperl_function(Oid fn_oid, bool is_trigger);
262+
static plperl_proc_desc *compile_plperl_function(Oid fn_oid,
263+
bool is_trigger,
264+
bool is_event_trigger);
261265

262266
static SV *plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc);
263267
static SV *plperl_hash_from_datum(Datum attr);
@@ -1610,6 +1614,23 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo)
16101614
}
16111615

16121616

1617+
/* Set up the arguments for an event trigger call. */
1618+
static SV *
1619+
plperl_event_trigger_build_args(FunctionCallInfo fcinfo)
1620+
{
1621+
EventTriggerData *tdata;
1622+
HV *hv;
1623+
1624+
hv = newHV();
1625+
1626+
tdata = (EventTriggerData *) fcinfo->context;
1627+
1628+
hv_store_string(hv, "event", cstr2sv(tdata->event));
1629+
hv_store_string(hv, "tag", cstr2sv(tdata->tag));
1630+
1631+
return newRV_noinc((SV *) hv);
1632+
}
1633+
16131634
/* Set up the new tuple returned from a trigger. */
16141635

16151636
static HeapTuple
@@ -1717,6 +1738,11 @@ plperl_call_handler(PG_FUNCTION_ARGS)
17171738
current_call_data = &this_call_data;
17181739
if (CALLED_AS_TRIGGER(fcinfo))
17191740
retval = PointerGetDatum(plperl_trigger_handler(fcinfo));
1741+
else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
1742+
{
1743+
plperl_event_trigger_handler(fcinfo);
1744+
retval = (Datum) 0;
1745+
}
17201746
else
17211747
retval = plperl_func_handler(fcinfo);
17221748
}
@@ -1853,7 +1879,8 @@ plperl_validator(PG_FUNCTION_ARGS)
18531879
Oid *argtypes;
18541880
char **argnames;
18551881
char *argmodes;
1856-
bool istrigger = false;
1882+
bool is_trigger = false;
1883+
bool is_event_trigger = false;
18571884
int i;
18581885

18591886
/* Get the new function's pg_proc entry */
@@ -1865,13 +1892,15 @@ plperl_validator(PG_FUNCTION_ARGS)
18651892
functyptype = get_typtype(proc->prorettype);
18661893

18671894
/* Disallow pseudotype result */
1868-
/* except for TRIGGER, RECORD, or VOID */
1895+
/* except for TRIGGER, EVTTRIGGER, RECORD, or VOID */
18691896
if (functyptype == TYPTYPE_PSEUDO)
18701897
{
18711898
/* we assume OPAQUE with no arguments means a trigger */
18721899
if (proc->prorettype == TRIGGEROID ||
18731900
(proc->prorettype == OPAQUEOID && proc->pronargs == 0))
1874-
istrigger = true;
1901+
is_trigger = true;
1902+
else if (proc->prorettype == EVTTRIGGEROID)
1903+
is_event_trigger = true;
18751904
else if (proc->prorettype != RECORDOID &&
18761905
proc->prorettype != VOIDOID)
18771906
ereport(ERROR,
@@ -1898,7 +1927,7 @@ plperl_validator(PG_FUNCTION_ARGS)
18981927
/* Postpone body checks if !check_function_bodies */
18991928
if (check_function_bodies)
19001929
{
1901-
(void) compile_plperl_function(funcoid, istrigger);
1930+
(void) compile_plperl_function(funcoid, is_trigger, is_event_trigger);
19021931
}
19031932

19041933
/* the result of a validator is ignored */
@@ -2169,6 +2198,63 @@ plperl_call_perl_trigger_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo,
21692198
}
21702199

21712200

2201+
static void
2202+
plperl_call_perl_event_trigger_func(plperl_proc_desc *desc,
2203+
FunctionCallInfo fcinfo,
2204+
SV *td)
2205+
{
2206+
dSP;
2207+
SV *retval,
2208+
*TDsv;
2209+
int count;
2210+
2211+
ENTER;
2212+
SAVETMPS;
2213+
2214+
TDsv = get_sv("main::_TD", 0);
2215+
if (!TDsv)
2216+
elog(ERROR, "couldn't fetch $_TD");
2217+
2218+
save_item(TDsv); /* local $_TD */
2219+
sv_setsv(TDsv, td);
2220+
2221+
PUSHMARK(sp);
2222+
PUTBACK;
2223+
2224+
/* Do NOT use G_KEEPERR here */
2225+
count = perl_call_sv(desc->reference, G_SCALAR | G_EVAL);
2226+
2227+
SPAGAIN;
2228+
2229+
if (count != 1)
2230+
{
2231+
PUTBACK;
2232+
FREETMPS;
2233+
LEAVE;
2234+
elog(ERROR, "didn't get a return item from trigger function");
2235+
}
2236+
2237+
if (SvTRUE(ERRSV))
2238+
{
2239+
(void) POPs;
2240+
PUTBACK;
2241+
FREETMPS;
2242+
LEAVE;
2243+
/* XXX need to find a way to assign an errcode here */
2244+
ereport(ERROR,
2245+
(errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV)))));
2246+
}
2247+
2248+
retval = newSVsv(POPs);
2249+
(void) retval; /* silence compiler warning */
2250+
2251+
PUTBACK;
2252+
FREETMPS;
2253+
LEAVE;
2254+
2255+
return;
2256+
}
2257+
21722258
static Datum
21732259
plperl_func_handler(PG_FUNCTION_ARGS)
21742260
{
@@ -2181,7 +2267,7 @@ plperl_func_handler(PG_FUNCTION_ARGS)
21812267
if (SPI_connect() != SPI_OK_CONNECT)
21822268
elog(ERROR, "could not connect to SPI manager");
21832269

2184-
prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false);
2270+
prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false, false);
21852271
current_call_data->prodesc = prodesc;
21862272
increment_prodesc_refcount(prodesc);
21872273

@@ -2295,7 +2381,7 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
22952381
elog(ERROR, "could not connect to SPI manager");
22962382

22972383
/* Find or compile the function */
2298-
prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, true);
2384+
prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, true, false);
22992385
current_call_data->prodesc = prodesc;
23002386
increment_prodesc_refcount(prodesc);
23012387

@@ -2386,6 +2472,45 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
23862472
}
23872473

23882474

2475+
static void
2476+
plperl_event_trigger_handler(PG_FUNCTION_ARGS)
2477+
{
2478+
plperl_proc_desc *prodesc;
2479+
SV *svTD;
2480+
ErrorContextCallback pl_error_context;
2481+
2482+
/* Connect to SPI manager */
2483+
if (SPI_connect() != SPI_OK_CONNECT)
2484+
elog(ERROR, "could not connect to SPI manager");
2485+
2486+
/* Find or compile the function */
2487+
prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false, true);
2488+
current_call_data->prodesc = prodesc;
2489+
increment_prodesc_refcount(prodesc);
2490+
2491+
/* Set a callback for error reporting */
2492+
pl_error_context.callback = plperl_exec_callback;
2493+
pl_error_context.previous = error_context_stack;
2494+
pl_error_context.arg = prodesc->proname;
2495+
error_context_stack = &pl_error_context;
2496+
2497+
activate_interpreter(prodesc->interp);
2498+
2499+
svTD = plperl_event_trigger_build_args(fcinfo);
2500+
plperl_call_perl_event_trigger_func(prodesc, fcinfo, svTD);
2501+
2502+
if (SPI_finish() != SPI_OK_FINISH)
2503+
elog(ERROR, "SPI_finish() failed");
2504+
2505+
/* Restore the previous error callback */
2506+
error_context_stack = pl_error_context.previous;
2507+
2508+
SvREFCNT_dec(svTD);
2509+
2510+
return;
2511+
}
2512+
2513+
23892514
static bool
23902515
validate_plperl_function(plperl_proc_ptr *proc_ptr, HeapTuple procTup)
23912516
{
@@ -2437,7 +2562,7 @@ free_plperl_function(plperl_proc_desc *prodesc)
24372562

24382563

24392564
static plperl_proc_desc *
2440-
compile_plperl_function(Oid fn_oid, bool is_trigger)
2565+
compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
24412566
{
24422567
HeapTuple procTup;
24432568
Form_pg_proc procStruct;
@@ -2543,7 +2668,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
25432668
* Get the required information for input conversion of the
25442669
* return value.
25452670
************************************************************/
2546-
if (!is_trigger)
2671+
if (!is_trigger && !is_event_trigger)
25472672
{
25482673
typeTup =
25492674
SearchSysCache1(TYPEOID,
@@ -2562,7 +2687,8 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
25622687
if (procStruct->prorettype == VOIDOID ||
25632688
procStruct->prorettype == RECORDOID)
25642689
/* okay */ ;
2565-
else if (procStruct->prorettype == TRIGGEROID)
2690+
else if (procStruct->prorettype == TRIGGEROID ||
2691+
procStruct->prorettype == EVTTRIGGEROID)
25662692
{
25672693
free_plperl_function(prodesc);
25682694
ereport(ERROR,
@@ -2598,7 +2724,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
25982724
* Get the required information for output conversion
25992725
* of all procedure arguments
26002726
************************************************************/
2601-
if (!is_trigger)
2727+
if (!is_trigger && !is_event_trigger)
26022728
{
26032729
prodesc->nargs = procStruct->pronargs;
26042730
for (i = 0; i < prodesc->nargs; i++)

src/pl/plperl/sql/plperl_trigger.sql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,23 @@ CREATE FUNCTION direct_trigger() RETURNS trigger AS $$
169169
$$ LANGUAGE plperl;
170170

171171
SELECT direct_trigger();
172+
173+
-- test plperl command triggers
174+
create or replace function perlsnitch() returns event_trigger language plperl as $$
175+
elog(NOTICE, "perlsnitch: " . $_TD->{event} . " " . $_TD->{tag} . " ");
176+
$$;
177+
178+
create event trigger perl_a_snitch on ddl_command_start
179+
execute procedure perlsnitch();
180+
create event trigger perl_b_snitch on ddl_command_end
181+
execute procedure perlsnitch();
182+
183+
create or replace function foobar() returns int language sql as $$select 1;$$;
184+
alter function foobar() cost 77;
185+
drop function foobar();
186+
187+
create table foo();
188+
drop table foo;
189+
190+
drop event trigger perl_a_snitch;
191+
drop event trigger perl_b_snitch;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy