<para>
Data type <type>RECORD</type>; variable holding the new
database row for <command>INSERT</command>/<command>UPDATE</command> operations in row-level
- triggers. This variable is unassigned in statement-level triggers
+ triggers. This variable is null in statement-level triggers
and for <command>DELETE</command> operations.
</para>
</listitem>
<para>
Data type <type>RECORD</type>; variable holding the old
database row for <command>UPDATE</command>/<command>DELETE</command> operations in row-level
- triggers. This variable is unassigned in statement-level triggers
+ triggers. This variable is null in statement-level triggers
and for <command>INSERT</command> operations.
</para>
</listitem>
</para>
</listitem>
+ <listitem>
+<!--
+2018-02-13 [4b93f5799] Make plpgsql use its DTYPE_REC code paths for composite-
+-->
+
+ <para>
+ In PL/pgSQL trigger functions, the <varname>OLD</varname>
+ and <varname>NEW</varname> variables now read as NULL when not
+ assigned (Tom Lane)
+ </para>
+
+ <para>
+ Previously, references to these variables could be parsed but not
+ executed.
+ </para>
+ </listitem>
+
</itemizedlist>
</sect2>
<listitem>
<!--
2018-02-13 [4b93f5799] Make plpgsql use its DTYPE_REC code paths for composite-
-
-->
<para>
REGRESS_OPTS = --dbname=$(PL_TESTDB)
REGRESS = plpgsql_call plpgsql_control plpgsql_domain plpgsql_record \
- plpgsql_cache plpgsql_transaction plpgsql_varprops
+ plpgsql_cache plpgsql_transaction plpgsql_trigger plpgsql_varprops
GEN_KEYWORDLIST = $(top_srcdir)/src/tools/gen_keywordlist.pl
--- /dev/null
+-- Simple test to verify accessibility of the OLD and NEW trigger variables
+create table testtr (a int, b text);
+create function testtr_trigger() returns trigger language plpgsql as
+$$begin
+ raise notice 'tg_op = %', tg_op;
+ raise notice 'old(%) = %', old.a, row(old.*);
+ raise notice 'new(%) = %', new.a, row(new.*);
+ if (tg_op = 'DELETE') then
+ return old;
+ else
+ return new;
+ end if;
+end$$;
+create trigger testtr_trigger before insert or delete or update on testtr
+ for each row execute function testtr_trigger();
+insert into testtr values (1, 'one'), (2, 'two');
+NOTICE: tg_op = INSERT
+NOTICE: old(<NULL>) = (,)
+NOTICE: new(1) = (1,one)
+NOTICE: tg_op = INSERT
+NOTICE: old(<NULL>) = (,)
+NOTICE: new(2) = (2,two)
+update testtr set a = a + 1;
+NOTICE: tg_op = UPDATE
+NOTICE: old(1) = (1,one)
+NOTICE: new(2) = (2,one)
+NOTICE: tg_op = UPDATE
+NOTICE: old(2) = (2,two)
+NOTICE: new(3) = (3,two)
+delete from testtr;
+NOTICE: tg_op = DELETE
+NOTICE: old(2) = (2,one)
+NOTICE: new(<NULL>) = (,)
+NOTICE: tg_op = DELETE
+NOTICE: old(3) = (3,two)
+NOTICE: new(<NULL>) = (,)
/*
* Put the OLD and NEW tuples into record variables
*
- * We make the tupdescs available in both records even though only one may
- * have a value. This allows parsing of record references to succeed in
- * functions that are used for multiple trigger types. For example, we
- * might have a test like "if (TG_OP = 'INSERT' and NEW.foo = 'xyz')",
- * which should parse regardless of the current trigger type.
+ * We set up expanded records for both variables even though only one may
+ * have a value. This allows record references to succeed in functions
+ * that are used for multiple trigger types. For example, we might have a
+ * test like "if (TG_OP = 'INSERT' and NEW.foo = 'xyz')", which should
+ * work regardless of the current trigger type. If a value is actually
+ * fetched from an unsupplied tuple, it will read as NULL.
*/
tupdesc = RelationGetDescr(trigdata->tg_relation);
--- /dev/null
+-- Simple test to verify accessibility of the OLD and NEW trigger variables
+
+create table testtr (a int, b text);
+
+create function testtr_trigger() returns trigger language plpgsql as
+$$begin
+ raise notice 'tg_op = %', tg_op;
+ raise notice 'old(%) = %', old.a, row(old.*);
+ raise notice 'new(%) = %', new.a, row(new.*);
+ if (tg_op = 'DELETE') then
+ return old;
+ else
+ return new;
+ end if;
+end$$;
+
+create trigger testtr_trigger before insert or delete or update on testtr
+ for each row execute function testtr_trigger();
+
+insert into testtr values (1, 'one'), (2, 'two');
+
+update testtr set a = a + 1;
+
+delete from testtr;