Re: TRIGGER with WHEN clause - Mailing list pgsql-hackers

From KaiGai Kohei
Subject Re: TRIGGER with WHEN clause
Date
Msg-id 4B0250FE.6070006@ak.jp.nec.com
Whole thread Raw
In response to Re: TRIGGER with WHEN clause  (Itagaki Takahiro <itagaki.takahiro@oss.ntt.co.jp>)
Responses Re: TRIGGER with WHEN clause
List pgsql-hackers
Itagaki-san, I checked the latest patch.

It seems to me the patch getting improved from the prior version.
We are next to the commiter review phase.

But I could find a few matters. :-(
Please see the following comments, and fix it before sending it
to commiters.

> I fixed the bug and two other bugs:
>   * Crash in AFTER TRIGGER + WHEN clause.
>   * Incorrect behavior when multiple tuples are modified.
> Also regression tests for it are added.

It looks for me fine.

> I'd like to add support statement triggers with WHEN clause.
> Of cource Oracle is a good example, but it doesn't prevent us
> from developing a better copy :)

OK, it is your decision.

>> * the documentation seems to me misleading.
>> It saids, NEW and OLD are only available and ...
>>  o INSERT can refer NEW
>>  o UPDATE can refer NEW, OLD
>>  o DELETE can refer OLD
>> But, it may actually incorrect, if user gives several events on a certain
>> trigger. For example, when a new trigger is invoked for each row on INSERT
>> or UPDATE statement, the function cannot refer the OLD.
> 
> They are bitwise-AND flags. INSERT-OR-DELETE trigger cannot refer to both
> OLD and NEW tuples. It is possible to use a dummy tuple (filled with NULLs?)
> in the cases, but I want to just throw an error as of now. I'll fix
> documentation to reflect the code. Ideas for better descriptions welcome.
> 
> | Note that if a trigger has multiple events, it can refer only tuples
> | that can be referred in all of the events. For example,
> | INSERT OR DELETE trigger cannot refer neither NEW nor OLD tuples.

At least, it seems to me meaningful.
Is there any comments from native English users?
      <varlistentry> +     <term><replaceable class="parameter">condition</replaceable></term> +     <listitem> +
<para>+       Any <acronym>SQL</acronym> conditional expression (returning +       <type>boolean</type>). Only
<literal>FOREACH ROW</literal> triggers +       can refer <literal>NEW</> and <literal>OLD</> tuples. +
<literal>INSERT</literal>trigger can refer <literal>NEW</>, +       <literal>DELETE</literal> trigger can refer
<literal>OLD</>,+       and <literal>UPDATE</literal> trigger can refer both of them. +       Note that if a trigger
hasmultiple events, it can refer only tuples +       that can be referred in all of the events. For example, +
<literal>INSERT</><literal>OR</> <literal>DELETE</> trigger cannot +       refer neither <literal>NEW</> nor
<literal>OLD</>tuples. +      </para> +     </listitem> +    </varlistentry>
 


In addition, I could find a few matters.

* TOAST may be necessary for pg_trigger?
----------------------------------------
If we give very looooooong condition on the WHEN clause, the pg_trigger.tgqual
can over the limitation of block size.
 postgres=# CREATE TRIGGER hoge before insert on t1 for row        when (a < [... very long condition ...])
executeprocedure trig_func(); ERROR:  row is too big: size 12940, maximum size 8164
 

But it is a quite corner case in my opinion. It depends on your preference.


* ROW INSERT TRIGGER on COPY FROM statement
-------------------------------------------
The Assert() in TriggerEnabled (commands/trigger.c:2410) was mistaken bombing.
In the code path from copy.c, NULL can be set on the estate->es_trig_tuple_slot.
 postgres=# CREATE TRIGGER tg_ins_row BEFORE INSERT ON t1 FOR ROW         WHEN (true) EXECUTE PROCEDURE trig_func();
CREATETRIGGER postgres=# COPY t1 FROM stdin; Enter data to be copied followed by a newline. End with a backslash and a
periodon a line by itself. >> 2    bbb >> server closed the connection unexpectedly         This probably means the
serverterminated abnormally         before or while processing the request. The connection to the server was lost.
Attemptingreset: Succeeded.
 
 (gdb) bt #0  0x00cd4416 in __kernel_vsyscall () #1  0x009beba1 in raise (sig=6) at
../nptl/sysdeps/unix/sysv/linux/raise.c:64#2  0x009c046a in abort () at abort.c:92 #3  0x08330e7e in
ExceptionalCondition(conditionName=<value optimized out>, errorType=<value optimized out>,     fileName=<value
optimizedout>, lineNumber=<value optimized out>) at assert.c:57 #4  0x081956d9 in TriggerEnabled (trigger=<value
optimizedout>, event=<value optimized out>,     modifiedCols=<value optimized out>, estate=<value optimized out>,
tupdesc=<valueoptimized out>,     oldtup=<value optimized out>, newtup=<value optimized out>) at trigger.c:2410 #5
0x08196410in ExecBRInsertTriggers (estate=<value optimized out>, relinfo=<value optimized out>,     trigtuple=<value
optimizedout>) at trigger.c:1835 #6  0x08162e0e in CopyFrom (cstate=<value optimized out>) at copy.c:2137 #7  DoCopy
(cstate=<valueoptimized out>) at copy.c:1189 #8  0x0827c653 in ProcessUtility (parsetree=<value optimized out>,
queryString=<valueoptimized out>,     params=<value optimized out>, isTopLevel=<value optimized out>, dest=<value
optimizedout>,     completionTag=<value optimized out>) at utility.c:581 #9  0x0827931d in PortalRunUtility
(portal=0x94a0e4c,utilityStmt=0x944133c,     isTopLevel=<value optimized out>, dest=<value optimized out>,
completionTag=<valueoptimized out>)     at pquery.c:1192 #10 0x0827a227 in PortalRunMulti (portal=0x94a0e4c,
isTopLevel=<valueoptimized out>,     dest=<value optimized out>, altdest=<value optimized out>, completionTag=<value
optimizedout>)     at pquery.c:1299 #11 0x0827aa64 in PortalRun (portal=<value optimized out>, count=<value optimized
out>,    isTopLevel=<value optimized out>, dest=<value optimized out>, altdest=<value optimized out>,
completionTag=<valueoptimized out>) at pquery.c:823 #12 0x08276d80 in exec_simple_query (query_string=<value optimized
out>)at postgres.c:1046 #13 0x08277fd3 in PostgresMain (argc=<value optimized out>, argv=<value optimized out>,
username=<valueoptimized out>) at postgres.c:3624 #14 0x08241228 in BackendRun (port=<value optimized out>) at
postmaster.c:3366#15 BackendStartup (port=<value optimized out>) at postmaster.c:3073 #16 ServerLoop (port=<value
optimizedout>) at postmaster.c:1399 #17 0x08243c68 in PostmasterMain (argc=<value optimized out>, argv=<value optimized
out>)at postmaster.c:1064 #18 0x081e3d5f in main (argc=<value optimized out>, argv=0x93c5b38) at main.c:188
 
 2407      if (TRIGGER_FIRED_BY_INSERT(event) || TRIGGER_FIRED_BY_UPDATE(event)) 2408      { 2409
Assert(newtup!= NULL); 2410          Assert(estate->es_trig_tuple_slot != NULL);  <---- (*) 2411 2412          newslot
=estate->es_trig_tuple_slot; 2413          if (newslot->tts_tupleDescriptor != tupdesc) 2414
ExecSetSlotDescriptor(newslot,tupdesc); 2415          if (newslot->tts_tuple != newtup) 2416
ExecStoreTuple(newtup,newslot, InvalidBuffer, false); 2417      }
 


* Using system column in WHEN clause
------------------------------------
If we use references to the system columns in the WHEN clause, its result may
be unexpected one from user's perspective.
 postgres=# CREATE TRIGGER tg_upd_row BEFORE UPDATE ON t1 FOR ROW         WHEN (OLD.oid != NEW.oid) EXECUTE PROCEDURE
trig_func();CREATE TRIGGER postgres=# UPDATE t1 SET b = b; NOTICE:  old=(1,aaa) new=(1,aaa) NOTICE:  old=(2,bbb)
new=(2,bbb)NOTICE:  old=(3,ccc) new=(3,ccc) UPDATE 3
 

From develope's perspective, it is quite natural because we know "oid" is
set within heap_update(), so these identifiers still have InvalidOid when
triggers are invoked.

I think we have two options for the matter:

1) Set correct values on the "oid" just before trigger invocations.  (However, what values should be visible for
"tableoid","xmin",   "ctid" and others? Is it really reasonable?)
 

2) Describe a notice on the user documentation not to use system columns  in the WHEN clause, because these are
assignedon after the trigger  invocations.
 

Here was a similar issue on the discussion related to suppress_redundant_updates_trigger().
See the following thread: http://archives.postgresql.org/pgsql-hackers/2008-11/thrd7.php#00273

In this case, we put an expected oid prior to comparison.
However, note that it is a special purpose function to avoid nonsense
updating, so this kind of workaround performs fine.
In my opinion, the (2) is more reasonable solution.

Thanks,
-- 
OSS Platform Development Division, NEC
KaiGai Kohei <kaigai@ak.jp.nec.com>


pgsql-hackers by date:

Previous
From: Peter Eisentraut
Date:
Subject: Re: UTF8 with BOM support in psql
Next
From: "Markus Wanner"
Date:
Subject: Re: write ahead logging in standby (streaming replication)