Skip to content

Commit a5036ca

Browse files
committed
PL/Tcl: Add event trigger support
From: Dimitri Fontaine <dimitri@2ndQuadrant.fr>
1 parent 45e02e3 commit a5036ca

File tree

4 files changed

+188
-23
lines changed

4 files changed

+188
-23
lines changed

doc/src/sgml/pltcl.sgml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,65 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
711711
</para>
712712
</sect1>
713713

714+
<sect1 id="pltcl-event-trigger">
715+
<title>Event Trigger Procedures in PL/Tcl</title>
716+
717+
<indexterm>
718+
<primary>event trigger</primary>
719+
<secondary>in PL/Tcl</secondary>
720+
</indexterm>
721+
722+
<para>
723+
Event trigger procedures can be written in PL/Tcl.
724+
<productname>PostgreSQL</productname> requires that a procedure that is
725+
to be called as an event trigger must be declared as a function with no
726+
arguments and a return type of <literal>event_trigger</>.
727+
</para>
728+
<para>
729+
The information from the trigger manager is passed to the procedure body
730+
in the following variables:
731+
732+
<variablelist>
733+
734+
<varlistentry>
735+
<term><varname>$TG_event</varname></term>
736+
<listitem>
737+
<para>
738+
The name of the event the trigger is fired for.
739+
</para>
740+
</listitem>
741+
</varlistentry>
742+
743+
<varlistentry>
744+
<term><varname>$TG_tag</varname></term>
745+
<listitem>
746+
<para>
747+
The command tag for which the trigger is fired.
748+
</para>
749+
</listitem>
750+
</varlistentry>
751+
</variablelist>
752+
</para>
753+
754+
<para>
755+
The return value of the trigger procedure is ignored.
756+
</para>
757+
758+
<para>
759+
Here's a little example event trigger procedure that simply raises
760+
a <literal>NOTICE</literal> message each time a supported command is
761+
executed:
762+
763+
<programlisting>
764+
CREATE OR REPLACE FUNCTION tclsnitch() RETURNS event_trigger AS $$
765+
elog NOTICE "tclsnitch: $TG_event $TG_tag"
766+
$$ LANGUAGE pltcl;
767+
768+
CREATE EVENT TRIGGER tcl_a_snitch ON ddl_command_start EXECUTE PROCEDURE tclsnitch();
769+
</programlisting>
770+
</para>
771+
</sect1>
772+
714773
<sect1 id="pltcl-unknown">
715774
<title>Modules and the <function>unknown</> Command</title>
716775
<para>

src/pl/tcl/expected/pltcl_setup.out

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,3 +519,26 @@ select tcl_date_week(2001,10,24);
519519
42
520520
(1 row)
521521

522+
-- test pltcl event triggers
523+
create or replace function tclsnitch() returns event_trigger language pltcl as $$
524+
elog NOTICE "tclsnitch: $TG_event $TG_tag"
525+
$$;
526+
create event trigger tcl_a_snitch on ddl_command_start execute procedure tclsnitch();
527+
create event trigger tcl_b_snitch on ddl_command_end execute procedure tclsnitch();
528+
create or replace function foobar() returns int language sql as $$select 1;$$;
529+
NOTICE: tclsnitch: ddl_command_start CREATE FUNCTION
530+
NOTICE: tclsnitch: ddl_command_end CREATE FUNCTION
531+
alter function foobar() cost 77;
532+
NOTICE: tclsnitch: ddl_command_start ALTER FUNCTION
533+
NOTICE: tclsnitch: ddl_command_end ALTER FUNCTION
534+
drop function foobar();
535+
NOTICE: tclsnitch: ddl_command_start DROP FUNCTION
536+
NOTICE: tclsnitch: ddl_command_end DROP FUNCTION
537+
create table foo();
538+
NOTICE: tclsnitch: ddl_command_start CREATE TABLE
539+
NOTICE: tclsnitch: ddl_command_end CREATE TABLE
540+
drop table foo;
541+
NOTICE: tclsnitch: ddl_command_start DROP TABLE
542+
NOTICE: tclsnitch: ddl_command_end DROP TABLE
543+
drop event trigger tcl_a_snitch;
544+
drop event trigger tcl_b_snitch;

src/pl/tcl/pltcl.c

Lines changed: 88 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "access/xact.h"
2828
#include "catalog/pg_proc.h"
2929
#include "catalog/pg_type.h"
30+
#include "commands/event_trigger.h"
3031
#include "commands/trigger.h"
3132
#include "executor/spi.h"
3233
#include "fmgr.h"
@@ -200,11 +201,13 @@ static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted);
200201
static Datum pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted);
201202

202203
static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted);
204+
static void pltcl_event_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted);
203205

204206
static void throw_tcl_error(Tcl_Interp *interp, const char *proname);
205207

206208
static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid,
207-
bool pltrusted);
209+
bool is_event_trigger,
210+
bool pltrusted);
208211

209212
static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
210213
int argc, CONST84 char *argv[]);
@@ -644,6 +647,12 @@ pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
644647
pltcl_current_fcinfo = NULL;
645648
retval = PointerGetDatum(pltcl_trigger_handler(fcinfo, pltrusted));
646649
}
650+
else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
651+
{
652+
pltcl_current_fcinfo = NULL;
653+
pltcl_event_trigger_handler(fcinfo, pltrusted);
654+
retval = (Datum) 0;
655+
}
647656
else
648657
{
649658
pltcl_current_fcinfo = fcinfo;
@@ -685,7 +694,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted)
685694

686695
/* Find or compile the function */
687696
prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
688-
pltrusted);
697+
false, pltrusted);
689698

690699
pltcl_current_prodesc = prodesc;
691700

@@ -844,6 +853,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
844853
/* Find or compile the function */
845854
prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
846855
RelationGetRelid(trigdata->tg_relation),
856+
false, /* not an event trigger */
847857
pltrusted);
848858

849859
pltcl_current_prodesc = prodesc;
@@ -1130,6 +1140,47 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
11301140
return rettup;
11311141
}
11321142

1143+
/**********************************************************************
1144+
* pltcl_event_trigger_handler() - Handler for event trigger calls
1145+
**********************************************************************/
1146+
static void
1147+
pltcl_event_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
1148+
{
1149+
pltcl_proc_desc *prodesc;
1150+
Tcl_Interp *volatile interp;
1151+
EventTriggerData *tdata = (EventTriggerData *) fcinfo->context;
1152+
Tcl_DString tcl_cmd;
1153+
int tcl_rc;
1154+
1155+
/* Connect to SPI manager */
1156+
if (SPI_connect() != SPI_OK_CONNECT)
1157+
elog(ERROR, "could not connect to SPI manager");
1158+
1159+
/* Find or compile the function */
1160+
prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
1161+
InvalidOid, true, pltrusted);
1162+
1163+
pltcl_current_prodesc = prodesc;
1164+
1165+
interp = prodesc->interp_desc->interp;
1166+
1167+
/* Create the tcl command and call the internal proc */
1168+
Tcl_DStringInit(&tcl_cmd);
1169+
Tcl_DStringAppendElement(&tcl_cmd, prodesc->internal_proname);
1170+
Tcl_DStringAppendElement(&tcl_cmd, tdata->event);
1171+
Tcl_DStringAppendElement(&tcl_cmd, tdata->tag);
1172+
1173+
tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
1174+
Tcl_DStringFree(&tcl_cmd);
1175+
1176+
/* Check for errors reported by Tcl. */
1177+
if (tcl_rc != TCL_OK)
1178+
throw_tcl_error(interp, prodesc->user_proname);
1179+
1180+
if (SPI_finish() != SPI_OK_FINISH)
1181+
elog(ERROR, "SPI_finish() failed");
1182+
}
1183+
11331184

11341185
/**********************************************************************
11351186
* throw_tcl_error - ereport an error returned from the Tcl interpreter
@@ -1168,7 +1219,8 @@ throw_tcl_error(Tcl_Interp *interp, const char *proname)
11681219
* (InvalidOid) when compiling a plain function.
11691220
**********************************************************************/
11701221
static pltcl_proc_desc *
1171-
compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
1222+
compile_pltcl_function(Oid fn_oid, Oid tgreloid,
1223+
bool is_event_trigger, bool pltrusted)
11721224
{
11731225
HeapTuple procTup;
11741226
Form_pg_proc procStruct;
@@ -1245,10 +1297,13 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
12451297
* "_trigger" when appropriate to ensure the normal and trigger
12461298
* cases are kept separate.
12471299
************************************************************/
1248-
if (!is_trigger)
1300+
if (!is_trigger && !is_event_trigger)
12491301
snprintf(internal_proname, sizeof(internal_proname),
12501302
"__PLTcl_proc_%u", fn_oid);
1251-
else
1303+
else if (is_event_trigger)
1304+
snprintf(internal_proname, sizeof(internal_proname),
1305+
"__PLTcl_proc_%u_evttrigger", fn_oid);
1306+
else if (is_trigger)
12521307
snprintf(internal_proname, sizeof(internal_proname),
12531308
"__PLTcl_proc_%u_trigger", fn_oid);
12541309

@@ -1286,7 +1341,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
12861341
* Get the required information for input conversion of the
12871342
* return value.
12881343
************************************************************/
1289-
if (!is_trigger)
1344+
if (!is_trigger && !is_event_trigger)
12901345
{
12911346
typeTup =
12921347
SearchSysCache1(TYPEOID,
@@ -1306,7 +1361,8 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
13061361
{
13071362
if (procStruct->prorettype == VOIDOID)
13081363
/* okay */ ;
1309-
else if (procStruct->prorettype == TRIGGEROID)
1364+
else if (procStruct->prorettype == TRIGGEROID ||
1365+
procStruct->prorettype == EVTTRIGGEROID)
13101366
{
13111367
free(prodesc->user_proname);
13121368
free(prodesc->internal_proname);
@@ -1347,7 +1403,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
13471403
* Get the required information for output conversion
13481404
* of all procedure arguments
13491405
************************************************************/
1350-
if (!is_trigger)
1406+
if (!is_trigger && !is_event_trigger)
13511407
{
13521408
prodesc->nargs = procStruct->pronargs;
13531409
proc_internal_args[0] = '\0';
@@ -1397,12 +1453,17 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
13971453
ReleaseSysCache(typeTup);
13981454
}
13991455
}
1400-
else
1456+
else if (is_trigger)
14011457
{
14021458
/* trigger procedure has fixed args */
14031459
strcpy(proc_internal_args,
14041460
"TG_name TG_relid TG_table_name TG_table_schema TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args");
14051461
}
1462+
else if (is_event_trigger)
1463+
{
1464+
/* event trigger procedure has fixed args */
1465+
strcpy(proc_internal_args, "TG_event TG_tag");
1466+
}
14061467

14071468
/************************************************************
14081469
* Create the tcl command to define the internal
@@ -1422,20 +1483,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
14221483
Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
14231484
Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
14241485
Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
1425-
if (!is_trigger)
1426-
{
1427-
for (i = 0; i < prodesc->nargs; i++)
1428-
{
1429-
if (prodesc->arg_is_rowtype[i])
1430-
{
1431-
snprintf(buf, sizeof(buf),
1432-
"array set %d $__PLTcl_Tup_%d\n",
1433-
i + 1, i + 1);
1434-
Tcl_DStringAppend(&proc_internal_body, buf, -1);
1435-
}
1436-
}
1437-
}
1438-
else
1486+
if (is_trigger)
14391487
{
14401488
Tcl_DStringAppend(&proc_internal_body,
14411489
"array set NEW $__PLTcl_Tup_NEW\n", -1);
@@ -1451,6 +1499,23 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
14511499
"}\n"
14521500
"unset i v\n\n", -1);
14531501
}
1502+
else if (is_event_trigger)
1503+
{
1504+
/* no argument support for event triggers */
1505+
}
1506+
else
1507+
{
1508+
for (i = 0; i < prodesc->nargs; i++)
1509+
{
1510+
if (prodesc->arg_is_rowtype[i])
1511+
{
1512+
snprintf(buf, sizeof(buf),
1513+
"array set %d $__PLTcl_Tup_%d\n",
1514+
i + 1, i + 1);
1515+
Tcl_DStringAppend(&proc_internal_body, buf, -1);
1516+
}
1517+
}
1518+
}
14541519

14551520
/************************************************************
14561521
* Add user's function definition to proc body

src/pl/tcl/sql/pltcl_setup.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,3 +559,21 @@ $$ language pltcl immutable;
559559

560560
select tcl_date_week(2010,1,24);
561561
select tcl_date_week(2001,10,24);
562+
563+
-- test pltcl event triggers
564+
create or replace function tclsnitch() returns event_trigger language pltcl as $$
565+
elog NOTICE "tclsnitch: $TG_event $TG_tag"
566+
$$;
567+
568+
create event trigger tcl_a_snitch on ddl_command_start execute procedure tclsnitch();
569+
create event trigger tcl_b_snitch on ddl_command_end execute procedure tclsnitch();
570+
571+
create or replace function foobar() returns int language sql as $$select 1;$$;
572+
alter function foobar() cost 77;
573+
drop function foobar();
574+
575+
create table foo();
576+
drop table foo;
577+
578+
drop event trigger tcl_a_snitch;
579+
drop event trigger tcl_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