Skip to content

Commit a69edc7

Browse files
pjungwirCommitfest Bot
authored and
Commitfest Bot
committed
Expose FOR PORTION OF to plpgsql triggers
It is helpful for triggers to see what the FOR PORTION OF clause specified: both the column/period name and the targeted bounds. Our RI triggers require this information, and we are passing it as part of the TriggerData struct. This commit allows plpgsql triggers functions to access the same information, using the new TG_PORTION_COLUMN and TG_PORTION_TARGET variables. Author: Paul Jungwirth
1 parent 0da03f6 commit a69edc7

File tree

6 files changed

+115
-26
lines changed

6 files changed

+115
-26
lines changed

doc/src/sgml/plpgsql.sgml

+24
Original file line numberDiff line numberDiff line change
@@ -4247,6 +4247,30 @@ ASSERT <replaceable class="parameter">condition</replaceable> <optional> , <repl
42474247
</para>
42484248
</listitem>
42494249
</varlistentry>
4250+
4251+
<varlistentry id="plpgsql-dml-trigger-tg-temporal-column">
4252+
<term><varname>TG_PERIOD_NAME</varname> <type>text</type></term>
4253+
<listitem>
4254+
<para>
4255+
the column name used in a <literal>FOR PORTION OF</literal> clause,
4256+
or else <symbol>NULL</symbol>.
4257+
</para>
4258+
</listitem>
4259+
</varlistentry>
4260+
4261+
<varlistentry id="plpgsql-dml-trigger-tg-temporal-target">
4262+
<term><varname>TG_PERIOD_BOUNDS</varname> <type>text</type></term>
4263+
<listitem>
4264+
<para>
4265+
the range/multirange/etc. given as the bounds of a
4266+
<literal>FOR PORTION OF</literal> clause, either directly (with parens syntax)
4267+
or computed from the <literal>FROM</literal> and <literal>TO</literal> bounds.
4268+
<symbol>NULL</symbol> if <literal>FOR PORTION OF</literal> was not used.
4269+
This is a text value based on the type's output function,
4270+
since the type can't be known at function creation time.
4271+
</para>
4272+
</listitem>
4273+
</varlistentry>
42504274
</variablelist>
42514275
</para>
42524276

src/pl/plpgsql/src/pl_comp.c

+27
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,33 @@ do_compile(FunctionCallInfo fcinfo,
724724
var->dtype = PLPGSQL_DTYPE_PROMISE;
725725
((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_ARGV;
726726

727+
/* Add the variable tg_period_name */
728+
var = plpgsql_build_variable("tg_period_name", 0,
729+
plpgsql_build_datatype(TEXTOID,
730+
-1,
731+
function->fn_input_collation,
732+
NULL),
733+
true);
734+
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
735+
var->dtype = PLPGSQL_DTYPE_PROMISE;
736+
((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_PERIOD_NAME;
737+
738+
/*
739+
* Add the variable to tg_period_bounds.
740+
* This could be any rangetype or multirangetype
741+
* or user-supplied type,
742+
* so the best we can offer is a TEXT variable.
743+
*/
744+
var = plpgsql_build_variable("tg_period_bounds", 0,
745+
plpgsql_build_datatype(TEXTOID,
746+
-1,
747+
function->fn_input_collation,
748+
NULL),
749+
true);
750+
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
751+
var->dtype = PLPGSQL_DTYPE_PROMISE;
752+
((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_PERIOD_BOUNDS;
753+
727754
break;
728755

729756
case PLPGSQL_EVENT_TRIGGER:

src/pl/plpgsql/src/pl_exec.c

+32
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,7 @@ plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
13851385
PLpgSQL_var *var)
13861386
{
13871387
MemoryContext oldcontext;
1388+
ForPortionOfState *fpo;
13881389

13891390
if (var->promise == PLPGSQL_PROMISE_NONE)
13901391
return; /* nothing to do */
@@ -1516,6 +1517,37 @@ plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
15161517
}
15171518
break;
15181519

1520+
case PLPGSQL_PROMISE_TG_PERIOD_NAME:
1521+
if (estate->trigdata == NULL)
1522+
elog(ERROR, "trigger promise is not in a trigger function");
1523+
if (estate->trigdata->tg_temporal)
1524+
assign_text_var(estate, var, estate->trigdata->tg_temporal->fp_rangeName);
1525+
else
1526+
assign_simple_var(estate, var, (Datum) 0, true, false);
1527+
break;
1528+
1529+
case PLPGSQL_PROMISE_TG_PERIOD_BOUNDS:
1530+
fpo = estate->trigdata->tg_temporal;
1531+
1532+
if (estate->trigdata == NULL)
1533+
elog(ERROR, "trigger promise is not in a trigger function");
1534+
if (fpo)
1535+
{
1536+
1537+
Oid funcid;
1538+
bool varlena;
1539+
1540+
getTypeOutputInfo(fpo->fp_rangeType, &funcid, &varlena);
1541+
Assert(OidIsValid(funcid));
1542+
1543+
assign_text_var(estate, var,
1544+
OidOutputFunctionCall(funcid,
1545+
fpo->fp_targetRange));
1546+
}
1547+
else
1548+
assign_simple_var(estate, var, (Datum) 0, true, false);
1549+
break;
1550+
15191551
case PLPGSQL_PROMISE_TG_EVENT:
15201552
if (estate->evtrigdata == NULL)
15211553
elog(ERROR, "event trigger promise is not in an event trigger function");

src/pl/plpgsql/src/plpgsql.h

+2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ typedef enum PLpgSQL_promise_type
8484
PLPGSQL_PROMISE_TG_ARGV,
8585
PLPGSQL_PROMISE_TG_EVENT,
8686
PLPGSQL_PROMISE_TG_TAG,
87+
PLPGSQL_PROMISE_TG_PERIOD_NAME,
88+
PLPGSQL_PROMISE_TG_PERIOD_BOUNDS,
8789
} PLpgSQL_promise_type;
8890

8991
/*

src/test/regress/expected/for_portion_of.out

+22-20
Original file line numberDiff line numberDiff line change
@@ -512,16 +512,18 @@ RETURNS TRIGGER LANGUAGE plpgsql AS
512512
$$
513513
BEGIN
514514
IF TG_OP = 'INSERT' THEN
515-
RAISE NOTICE '%: % %, NEW table = %',
516-
TG_NAME, TG_OP, TG_LEVEL, (SELECT string_agg(new_table::text, ', ' ORDER BY id) FROM new_table);
515+
RAISE NOTICE '%: % FOR PORTION OF % (%) %, NEW table = %',
516+
TG_NAME, TG_OP, TG_PERIOD_NAME, TG_PERIOD_BOUNDS, TG_LEVEL,
517+
(SELECT string_agg(new_table::text, ', ' ORDER BY id) FROM new_table);
517518
ELSIF TG_OP = 'UPDATE' THEN
518-
RAISE NOTICE '%: % %, OLD table = %, NEW table = %',
519-
TG_NAME, TG_OP, TG_LEVEL,
519+
RAISE NOTICE '%: % FOR PORTION OF % (%) %, OLD table = %, NEW table = %',
520+
TG_NAME, TG_OP, TG_PERIOD_NAME, TG_PERIOD_BOUNDS, TG_LEVEL,
520521
(SELECT string_agg(old_table::text, ', ' ORDER BY id) FROM old_table),
521522
(SELECT string_agg(new_table::text, ', ' ORDER BY id) FROM new_table);
522523
ELSIF TG_OP = 'DELETE' THEN
523-
RAISE NOTICE '%: % %, OLD table = %',
524-
TG_NAME, TG_OP, TG_LEVEL, (SELECT string_agg(old_table::text, ', ' ORDER BY id) FROM old_table);
524+
RAISE NOTICE '%: % FOR PORTION OF % (%) %, OLD table = %',
525+
TG_NAME, TG_OP, TG_PERIOD_NAME, TG_PERIOD_BOUNDS, TG_LEVEL,
526+
(SELECT string_agg(old_table::text, ', ' ORDER BY id) FROM old_table);
525527
END IF;
526528
RETURN NULL;
527529
END;
@@ -554,29 +556,29 @@ BEGIN;
554556
UPDATE for_portion_of_test
555557
FOR PORTION OF valid_at FROM '2018-01-15' TO '2019-01-01'
556558
SET name = '2018-01-15_to_2019-01-01';
557-
NOTICE: for_portion_of_test_insert_trig: INSERT ROW, NEW table = ("[1,2)","[2018-01-01,2018-01-15)",one)
558-
NOTICE: for_portion_of_test_insert_trig_stmt: INSERT STATEMENT, NEW table = ("[1,2)","[2018-01-01,2018-01-15)",one)
559-
NOTICE: for_portion_of_test_insert_trig: INSERT ROW, NEW table = ("[1,2)","[2019-01-01,2020-01-01)",one)
560-
NOTICE: for_portion_of_test_insert_trig_stmt: INSERT STATEMENT, NEW table = ("[1,2)","[2019-01-01,2020-01-01)",one)
561-
NOTICE: for_portion_of_test_update_trig: UPDATE ROW, OLD table = ("[1,2)","[2018-01-01,2020-01-01)",one), NEW table = ("[1,2)","[2018-01-15,2019-01-01)",2018-01-15_to_2019-01-01)
562-
NOTICE: for_portion_of_test_update_trig_stmt: UPDATE STATEMENT, OLD table = ("[1,2)","[2018-01-01,2020-01-01)",one), NEW table = ("[1,2)","[2018-01-15,2019-01-01)",2018-01-15_to_2019-01-01)
559+
NOTICE: for_portion_of_test_insert_trig: INSERT FOR PORTION OF <NULL> (<NULL>) ROW, NEW table = ("[1,2)","[2018-01-01,2018-01-15)",one)
560+
NOTICE: for_portion_of_test_insert_trig_stmt: INSERT FOR PORTION OF <NULL> (<NULL>) STATEMENT, NEW table = ("[1,2)","[2018-01-01,2018-01-15)",one)
561+
NOTICE: for_portion_of_test_insert_trig: INSERT FOR PORTION OF <NULL> (<NULL>) ROW, NEW table = ("[1,2)","[2019-01-01,2020-01-01)",one)
562+
NOTICE: for_portion_of_test_insert_trig_stmt: INSERT FOR PORTION OF <NULL> (<NULL>) STATEMENT, NEW table = ("[1,2)","[2019-01-01,2020-01-01)",one)
563+
NOTICE: for_portion_of_test_update_trig: UPDATE FOR PORTION OF valid_at ([2018-01-15,2019-01-01)) ROW, OLD table = ("[1,2)","[2018-01-01,2020-01-01)",one), NEW table = ("[1,2)","[2018-01-15,2019-01-01)",2018-01-15_to_2019-01-01)
564+
NOTICE: for_portion_of_test_update_trig_stmt: UPDATE FOR PORTION OF valid_at ([2018-01-15,2019-01-01)) STATEMENT, OLD table = ("[1,2)","[2018-01-01,2020-01-01)",one), NEW table = ("[1,2)","[2018-01-15,2019-01-01)",2018-01-15_to_2019-01-01)
563565
ROLLBACK;
564566
BEGIN;
565567
DELETE FROM for_portion_of_test
566568
FOR PORTION OF valid_at FROM NULL TO '2018-01-21';
567-
NOTICE: for_portion_of_test_insert_trig: INSERT ROW, NEW table = ("[1,2)","[2018-01-21,2020-01-01)",one)
568-
NOTICE: for_portion_of_test_insert_trig_stmt: INSERT STATEMENT, NEW table = ("[1,2)","[2018-01-21,2020-01-01)",one)
569-
NOTICE: for_portion_of_test_delete_trig: DELETE ROW, OLD table = ("[1,2)","[2018-01-01,2020-01-01)",one)
570-
NOTICE: for_portion_of_test_delete_trig_stmt: DELETE STATEMENT, OLD table = ("[1,2)","[2018-01-01,2020-01-01)",one)
569+
NOTICE: for_portion_of_test_insert_trig: INSERT FOR PORTION OF <NULL> (<NULL>) ROW, NEW table = ("[1,2)","[2018-01-21,2020-01-01)",one)
570+
NOTICE: for_portion_of_test_insert_trig_stmt: INSERT FOR PORTION OF <NULL> (<NULL>) STATEMENT, NEW table = ("[1,2)","[2018-01-21,2020-01-01)",one)
571+
NOTICE: for_portion_of_test_delete_trig: DELETE FOR PORTION OF valid_at ((,2018-01-21)) ROW, OLD table = ("[1,2)","[2018-01-01,2020-01-01)",one)
572+
NOTICE: for_portion_of_test_delete_trig_stmt: DELETE FOR PORTION OF valid_at ((,2018-01-21)) STATEMENT, OLD table = ("[1,2)","[2018-01-01,2020-01-01)",one)
571573
ROLLBACK;
572574
BEGIN;
573575
UPDATE for_portion_of_test
574576
FOR PORTION OF valid_at FROM NULL TO '2018-01-02'
575577
SET name = 'NULL_to_2018-01-01';
576-
NOTICE: for_portion_of_test_insert_trig: INSERT ROW, NEW table = ("[1,2)","[2018-01-02,2020-01-01)",one)
577-
NOTICE: for_portion_of_test_insert_trig_stmt: INSERT STATEMENT, NEW table = ("[1,2)","[2018-01-02,2020-01-01)",one)
578-
NOTICE: for_portion_of_test_update_trig: UPDATE ROW, OLD table = ("[1,2)","[2018-01-01,2020-01-01)",one), NEW table = ("[1,2)","[2018-01-01,2018-01-02)",NULL_to_2018-01-01)
579-
NOTICE: for_portion_of_test_update_trig_stmt: UPDATE STATEMENT, OLD table = ("[1,2)","[2018-01-01,2020-01-01)",one), NEW table = ("[1,2)","[2018-01-01,2018-01-02)",NULL_to_2018-01-01)
578+
NOTICE: for_portion_of_test_insert_trig: INSERT FOR PORTION OF <NULL> (<NULL>) ROW, NEW table = ("[1,2)","[2018-01-02,2020-01-01)",one)
579+
NOTICE: for_portion_of_test_insert_trig_stmt: INSERT FOR PORTION OF <NULL> (<NULL>) STATEMENT, NEW table = ("[1,2)","[2018-01-02,2020-01-01)",one)
580+
NOTICE: for_portion_of_test_update_trig: UPDATE FOR PORTION OF valid_at ((,2018-01-02)) ROW, OLD table = ("[1,2)","[2018-01-01,2020-01-01)",one), NEW table = ("[1,2)","[2018-01-01,2018-01-02)",NULL_to_2018-01-01)
581+
NOTICE: for_portion_of_test_update_trig_stmt: UPDATE FOR PORTION OF valid_at ((,2018-01-02)) STATEMENT, OLD table = ("[1,2)","[2018-01-01,2020-01-01)",one), NEW table = ("[1,2)","[2018-01-01,2018-01-02)",NULL_to_2018-01-01)
580582
ROLLBACK;
581583
-- Test with multiranges
582584
CREATE TABLE for_portion_of_test2 (

src/test/regress/sql/for_portion_of.sql

+8-6
Original file line numberDiff line numberDiff line change
@@ -396,16 +396,18 @@ RETURNS TRIGGER LANGUAGE plpgsql AS
396396
$$
397397
BEGIN
398398
IF TG_OP = 'INSERT' THEN
399-
RAISE NOTICE '%: % %, NEW table = %',
400-
TG_NAME, TG_OP, TG_LEVEL, (SELECT string_agg(new_table::text, ', ' ORDER BY id) FROM new_table);
399+
RAISE NOTICE '%: % FOR PORTION OF % (%) %, NEW table = %',
400+
TG_NAME, TG_OP, TG_PERIOD_NAME, TG_PERIOD_BOUNDS, TG_LEVEL,
401+
(SELECT string_agg(new_table::text, ', ' ORDER BY id) FROM new_table);
401402
ELSIF TG_OP = 'UPDATE' THEN
402-
RAISE NOTICE '%: % %, OLD table = %, NEW table = %',
403-
TG_NAME, TG_OP, TG_LEVEL,
403+
RAISE NOTICE '%: % FOR PORTION OF % (%) %, OLD table = %, NEW table = %',
404+
TG_NAME, TG_OP, TG_PERIOD_NAME, TG_PERIOD_BOUNDS, TG_LEVEL,
404405
(SELECT string_agg(old_table::text, ', ' ORDER BY id) FROM old_table),
405406
(SELECT string_agg(new_table::text, ', ' ORDER BY id) FROM new_table);
406407
ELSIF TG_OP = 'DELETE' THEN
407-
RAISE NOTICE '%: % %, OLD table = %',
408-
TG_NAME, TG_OP, TG_LEVEL, (SELECT string_agg(old_table::text, ', ' ORDER BY id) FROM old_table);
408+
RAISE NOTICE '%: % FOR PORTION OF % (%) %, OLD table = %',
409+
TG_NAME, TG_OP, TG_PERIOD_NAME, TG_PERIOD_BOUNDS, TG_LEVEL,
410+
(SELECT string_agg(old_table::text, ', ' ORDER BY id) FROM old_table);
409411
END IF;
410412
RETURN NULL;
411413
END;

0 commit comments

Comments
 (0)