Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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

+59
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

+23
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

+88-23
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

+18
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)