Thread: Trigger function not executing

Trigger function not executing

From
"Donald Fraser"
Date:
OS: Redhat, kernel 2.4
PostgreSQL 7.3.1 on i686-pc-linux-gnu, compiled by GCC 2.96

I have a table that has two triggers functions applied to it
1) CREATE TRIGGER trig_delete_user BEFORE DELETE ON tbl_user FOR EACH ROW E=
XECUTE PROCEDURE delete_user();
2) CREATE TRIGGER trig_insert_sys_user AFTER INSERT OR DELETE OR UPDATE ON =
tbl_user FOR EACH ROW EXECUTE PROCEDURE insert_sys_user();

The BEFORE trigger function basically does nothing to modify the tuple in a=
ny way. It does run a SELECT on the same table to do some routine checks an=
d before it can exit the function it sets a global flag to tell the AFTER t=
rigger function what to do.

The AFTER trigger function resets the flag set by the BEFORE function and t=
hen performs some administration commands for users . In this instance it i=
ssues a "DROP USER" command.

I've included the above summary as an insight to where the problem may be.

Here is the problem:
If I run SQL such as DELETE FROM tbl_user WHERE id =3D '2'; then I don't ge=
t any problems.
If I run SQL such as DELETE FROM tbl_user WHERE id > '40'; and there is onl=
y one record that meets the condition (id > '40') then I don't get any prob=
lems.
If I run SQL such as DELETE FROM tbl_user WHERE id > '20'; and there is mor=
e than one record that meets the condition (id > '20') then I get the follo=
wing problem. The BEFORE trigger function is only being called on the first=
 record that is being deleted yet the AFTER function is being called on all=
 of the records - well in this case it only gets to the second record and f=
ails because the flag has not been set by the BEFORE function. Just to re-i=
terate - the BFORE function is very simple - it cannot exit the function wi=
thout setting the flag I have mentioned.

Have I misunderstood trigger functions or is this a legitimate bug?

Regards
Donald Fraser

Re: Trigger function not executing

From
Tom Lane
Date:
"Donald Fraser" <demolish@cwgsy.net> writes:
> If I run SQL such as DELETE FROM tbl_user WHERE id > '20'; and there is mor=
> e than one record that meets the condition (id > '20') then I get the follo=
> wing problem. The BEFORE trigger function is only being called on the first=
>  record that is being deleted

I don't believe it --- and you haven't provided any evidence to support
your claim.  More than likely, this is just an error in your own trigger
code; but with no details it's pointless to speculate.

            regards, tom lane

Re: Trigger function not executing

From
"Donald Fraser"
Date:
----- Original Message -----=20
  From: Donald Fraser=20
  To: pgsql-bugs@postgresql.org=20
  Sent: Thursday, February 06, 2003 11:27 AM
  Subject: [BUGS] Trigger function not executing



  OS: Redhat, kernel 2.4
  PostgreSQL 7.3.1 on i686-pc-linux-gnu, compiled by GCC 2.96

  I have a table that has two triggers functions applied to it
  1) CREATE TRIGGER trig_delete_user BEFORE DELETE ON tbl_user FOR EACH ROW=
 EXECUTE PROCEDURE delete_user();
  2) CREATE TRIGGER trig_insert_sys_user AFTER INSERT OR DELETE OR UPDATE O=
N tbl_user FOR EACH ROW EXECUTE PROCEDURE insert_sys_user();

  The BEFORE trigger function basically does nothing to modify the tuple in=
 any way. It does run a SELECT on the same table to do some routine checks =
and before it can exit the function it sets a global flag to tell the AFTER=
 trigger function what to do.

  The AFTER trigger function resets the flag set by the BEFORE function and=
 then performs some administration commands for users . In this instance it=
 issues a "DROP USER" command.

  I've included the above summary as an insight to where the problem may be.

  Here is the problem:
  If I run SQL such as DELETE FROM tbl_user WHERE id =3D '2'; then I don't =
get any problems.
  If I run SQL such as DELETE FROM tbl_user WHERE id > '40'; and there is o=
nly one record that meets the condition (id > '40') then I don't get any pr=
oblems.
  If I run SQL such as DELETE FROM tbl_user WHERE id > '20'; and there is m=
ore than one record that meets the condition (id > '20') then I get the fol=
lowing problem. The BEFORE trigger function is only being called on the fir=
st record that is being deleted yet the AFTER function is being called on a=
ll of the records - well in this case it only gets to the second record and=
 fails because the flag has not been set by the BEFORE function. Just to re=
-iterate - the BFORE function is very simple - it cannot exit the function =
without setting the flag I have mentioned.

  Have I misunderstood trigger functions or is this a legitimate bug?

  Regards
  Donald Fraser

  ----- Original Message -----=20
  From: "Tom Lane" <tgl@sss.pgh.pa.us>
  To: "Donald Fraser" <demolish@cwgsy.net>
  Cc: <pgsql-bugs@postgresql.org>
  Sent: Thursday, February 06, 2003 2:42 PM
  Subject: Re: [BUGS] Trigger function not executing=20


  > "Donald Fraser" <demolish@cwgsy.net> writes:
  > > If I run SQL such as DELETE FROM tbl_user WHERE id > '20'; and there =
is mor=3D
  > > e than one record that meets the condition (id > '20') then I get the=
 follo=3D
  > > wing problem. The BEFORE trigger function is only being called on the=
 first=3D
  > >  record that is being deleted=20
  >=20
  > I don't believe it --- and you haven't provided any evidence to support
  > your claim.  More than likely, this is just an error in your own trigger
  > code; but with no details it's pointless to speculate.
  >=20
  > regards, tom lane
  >=20
Tom I have had another look at this problem and you were correct in that th=
e trigger functions were being called, just not in the order that I had exp=
ected. There were "no bugs" in my code as such, just a total misunderstandi=
ng on how trigger functions are processed in a SQL command that effects mor=
e than one record - hence the reason why I'm writing this email. I don't wa=
nt other people to be caught out with the same problem and it may well be w=
orth putting a note in the documentation under the trigger manager section =
and correcting the example in Chapter 16.4.=20

I made the following assumption (based on the documents): The sequence of e=
vents for trigger functions would be exactly the same regardless of the SQL=
 statement/s to achieve the same result. That is I thought that for example=
: DELETE FROM tbl_user WHERE id =3D '20'; DELETE FROM tbl_user WHERE id =3D=
 '21';  would produce exactly the same sequence of events as DELETE FROM tb=
l_user WHERE (id >=3D '20' AND id <=3D '21'); This is not the case.

The sequence of events for the first set of SQL statements is as follows:
1: Trigger BEFORE EVENT for record with id =3D 20;
2: Trigger AFTER EVENT for record with id =3D 20;
3: Trigger BEFORE EVENT for record with id =3D 21;
4: Trigger AFTER EVENT for record with id =3D 21;

The sequence of events for the second set of SQL statements is as follows:
1: Trigger BEFORE EVENT for record with id =3D 20;
2: Trigger BEFORE EVENT for record with id =3D 21;
3: Trigger AFTER EVENT for record with id =3D 20;
4: Trigger AFTER EVENT for record with id =3D 21;

According to the example in the documentation - 'Chapter 16.4 Examples', th=
e sequence of events for the DELETE is as follows:
vac=3D> DELETE FROM ttest;
INFO:  trigf (fired before): there are 2 tuples in ttest
INFO:  trigf (fired after ): there are 1 tuples in ttest
INFO:  trigf (fired before): there are 1 tuples in ttest
INFO:  trigf (fired after ): there are 0 tuples in ttest

So you can see that the documented sequence of events is not the same as wh=
at actually happens.
To prove this I created and ran the example from the documentation (cut and=
 paste exactly from the documentation).
Here is the output that I got:

CISX=3D# INSERT INTO ttest VALUES('100');
NOTICE: trigf (fired before): there are 0 tuples in ttest
NOTICE: trigf (fired after ): there are 1 tuples in ttest
INSERT 17497 1
CISX=3D# INSERT INTO ttest VALUES('101');
NOTICE: trigf (fired before): there are 1 tuples in ttest
NOTICE: trigf (fired after ): there are 2 tuples in ttest
INSERT 17498 1
CISX=3D# INSERT INTO ttest VALUES('102');
NOTICE: trigf (fired before): there are 2 tuples in ttest
NOTICE: trigf (fired after ): there are 3 tuples in ttest
INSERT 17499 1
CISX=3D# INSERT INTO ttest VALUES('122');
NOTICE: trigf (fired before): there are 3 tuples in ttest
NOTICE: trigf (fired after ): there are 4 tuples in ttest
INSERT 17500 1
CISX=3D# DELETE FROM ttest WHERE x >=3D '100';
NOTICE: trigf (fired before): there are 4 tuples in ttest
NOTICE: trigf (fired before): there are 3 tuples in ttest
NOTICE: trigf (fired before): there are 2 tuples in ttest
NOTICE: trigf (fired before): there are 1 tuples in ttest
NOTICE: trigf (fired after ): there are 0 tuples in ttest
NOTICE: trigf (fired after ): there are 0 tuples in ttest
NOTICE: trigf (fired after ): there are 0 tuples in ttest
NOTICE: trigf (fired after ): there are 0 tuples in ttest
DELETE 4

As you can see the actual sequence of events differs to what is documented =
and what I had logically expected - hence why I was questioning whether the=
re was a bug with triggers, albeit that I was pointing the finger at the wr=
ong thing. Next time I want be so keen to come forward with a bug without i=
nvestigating it further - sorry for that.

Regards
Donald Fraser.

Re: Trigger function not executing

From
Tom Lane
Date:
"Donald Fraser" <demolish@cwgsy.net> writes:
>   The BEFORE trigger function basically does nothing to modify the tuple in=
>  any way. It does run a SELECT on the same table to do some routine checks =
> and before it can exit the function it sets a global flag to tell the AFTER=
>  trigger function what to do.
>   The AFTER trigger function resets the flag set by the BEFORE function and=
>  then performs some administration commands for users . In this instance it=
>  issues a "DROP USER" command.

And this global flag is stored how?

>   Have I misunderstood trigger functions or is this a legitimate bug?

I suspect you have failed to consider that the AFTER trigger fires once
per modified row.  If the "global flag" is really global, then it will
appear cleared to all but the first firing.

I am not sure what your intentions are with all this, but from here
it looks suspiciously like you are inventing a lot of unnecessary
mechanism.  Why don't you just drop the user in the BEFORE trigger?
Or forget the BEFORE trigger and drop the user in the AFTER trigger?
I can't see any reason for two triggers.  I also wonder whether you've
aware that DROP USER is transactional --- if the transaction rolls back
due to error, so does the DROP.  So I don't see any strong need not to
just do the DROP in the BEFORE trigger.

            regards, tom lane