On Thu, May 5, 2011 at 06:51, Alvaro Herrera <alvhe...@commandprompt.com> wrote:
> Excerpts from Alex Hunsaker's message of mié may 04 23:53:34 -0300 2011:
>
>> After playing with it a bit more I see 2 clear options:
>> 1) make $_TD global like %_SHARED. This should not cause any problems
>> as we make $_TD private via local() before each trigger call. Also pre
>> 9.1 non trigger functions could still access and check the definedness
>> of $_TD so if someone was relying on that (for whatever unknown
>> reason) that will work again.
>
> This is strange.  Are you saying that there's no decent way to make a
> variable global in C code?

Im sure we could... I don't see any reason to do it in C. (performance
or otherwise)

In other news I found another bug with this-- it was trying to
local($_TD) by using SAVESPTR() when it seems it really should be
using save_item(). Currently its not really localizing $_TD, which at
the very least means recursive triggers might modify the callers $_TD.
Ugh.

Fixed in the attached plus added regression tests for both issues (use
strict; && Global symbol "$_TD" requires explicit package name, test
recursive trigger calls). Although Ill admit, given the point we are
in the release I could see a revert also being justified.

Greg, big thanks for testing! keep it up! :)
*** a/src/pl/plperl/expected/plperl_trigger.out
--- b/src/pl/plperl/expected/plperl_trigger.out
***************
*** 255,260 **** SELECT * FROM trigger_test;
--- 255,289 ----
   5 | third line(modified by trigger)(modified by trigger) | ("(5)")
  (4 rows)
  
+ DROP TRIGGER "test_valid_id_trig" ON trigger_test;
+ CREATE OR REPLACE FUNCTION trigger_recurse() RETURNS trigger AS $$
+ 	use strict;
+ 
+ 	if ($_TD->{new}{i} == 10000)
+ 	{
+ 		spi_exec_query("insert into trigger_test (i, v) values (20000, 'child');");
+ 
+ 		if ($_TD->{new}{i} != 10000)
+ 		{
+ 			die "recursive trigger modified: ". $_TD->{new}{i};
+ 		}
+ 	}
+     return;
+ $$ LANGUAGE plperl;
+ CREATE TRIGGER "test_trigger_recurse" BEFORE INSERT ON trigger_test
+ FOR EACH ROW EXECUTE PROCEDURE "trigger_recurse"();
+ INSERT INTO trigger_test (i, v) values (10000, 'top');
+ SELECT * FROM trigger_test;
+    i   |                          v                           |   foo   
+ -------+------------------------------------------------------+---------
+      1 | first line(modified by trigger)                      | ("(2)")
+      2 | second line(modified by trigger)                     | ("(3)")
+      4 | immortal                                             | ("(4)")
+      5 | third line(modified by trigger)(modified by trigger) | ("(5)")
+  20000 | child                                                | 
+  10000 | top                                                  | 
+ (6 rows)
+ 
  CREATE OR REPLACE FUNCTION immortal() RETURNS trigger AS $$
      if ($_TD->{old}{v} eq $_TD->{args}[0])
      {
*** a/src/pl/plperl/plc_perlboot.pl
--- b/src/pl/plperl/plc_perlboot.pl
***************
*** 1,7 ****
  #  src/pl/plperl/plc_perlboot.pl
  
  use 5.008001;
! use vars qw(%_SHARED);
  
  PostgreSQL::InServer::Util::bootstrap();
  
--- 1,7 ----
  #  src/pl/plperl/plc_perlboot.pl
  
  use 5.008001;
! use vars qw(%_SHARED $_TD);
  
  PostgreSQL::InServer::Util::bootstrap();
  
*** a/src/pl/plperl/plperl.c
--- b/src/pl/plperl/plperl.c
***************
*** 1976,1983 **** plperl_call_perl_trigger_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo,
  	ENTER;
  	SAVETMPS;
  
! 	TDsv = get_sv("_TD", GV_ADD);
! 	SAVESPTR(TDsv);				/* local $_TD */
  	sv_setsv(TDsv, td);
  
  	PUSHMARK(sp);
--- 1976,1986 ----
  	ENTER;
  	SAVETMPS;
  
! 	TDsv = get_sv("_TD", 0);
! 	if(!TDsv)
! 		elog(ERROR, "couldn't fetch $_TD");
! 
! 	save_item(TDsv);				/* local $_TD */
  	sv_setsv(TDsv, td);
  
  	PUSHMARK(sp);
*** a/src/pl/plperl/sql/plperl_trigger.sql
--- b/src/pl/plperl/sql/plperl_trigger.sql
***************
*** 122,127 **** UPDATE trigger_test SET i = 100 where i=1;
--- 122,151 ----
  
  SELECT * FROM trigger_test;
  
+ DROP TRIGGER "test_valid_id_trig" ON trigger_test;
+ 
+ CREATE OR REPLACE FUNCTION trigger_recurse() RETURNS trigger AS $$
+ 	use strict;
+ 
+ 	if ($_TD->{new}{i} == 10000)
+ 	{
+ 		spi_exec_query("insert into trigger_test (i, v) values (20000, 'child');");
+ 
+ 		if ($_TD->{new}{i} != 10000)
+ 		{
+ 			die "recursive trigger modified: ". $_TD->{new}{i};
+ 		}
+ 	}
+     return;
+ $$ LANGUAGE plperl;
+ 
+ CREATE TRIGGER "test_trigger_recurse" BEFORE INSERT ON trigger_test
+ FOR EACH ROW EXECUTE PROCEDURE "trigger_recurse"();
+ 
+ INSERT INTO trigger_test (i, v) values (10000, 'top');
+ 
+ SELECT * FROM trigger_test;
+ 
  CREATE OR REPLACE FUNCTION immortal() RETURNS trigger AS $$
      if ($_TD->{old}{v} eq $_TD->{args}[0])
      {
-- 
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

Reply via email to