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: