Miscellaneous changes to plperl [PATCH]

Started by Tim Buncealmost 16 years ago16 messages
#1Tim Bunce
Tim.Bunce@pobox.com
1 attachment(s)

This is the second of the patches to be split out from the former
'plperl feature patch 1'.

Changes in this patch:

- Allow (ineffective) use of 'require' in plperl
If the required module is not already loaded then it dies.
So "use strict;" now works in plperl.

- Pre-load the feature module if perl >= 5.10.
So "use feature :5.10;" now works in plperl.

- Stored procedure subs are now given names.
The names are not visible in ordinary use, but they make
tools like Devel::NYTProf and Devel::Cover _much_ more useful.

- Simplified and generalized the subroutine creation code.
Now one code path for generating sub source code, not four.
Can generate multiple 'use' statements with specific imports
(which handles plperl.use_strict currently and can easily
be extended to handle a plperl.use_feature=':5.12' in future).

- Disallows use of Safe version 2.20 which is broken for PL/Perl.
http://rt.perl.org/rt3/Ticket/Display.html?id=72068

- Assorted minor optimizations by pre-growing data structures.

This patch will apply cleanly over the 'add functions' patch:
https://commitfest.postgresql.org/action/patch_view?id=264

Tim.

Attachments:

plperl-misc.patchtext/x-patch; charset=us-asciiDownload
diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml
index 94db722..6fee031 100644
*** a/doc/src/sgml/plperl.sgml
--- b/doc/src/sgml/plperl.sgml
*************** SELECT * FROM perl_set();
*** 285,313 ****
    </para>
  
    <para>
!    If you wish to use the <literal>strict</> pragma with your code,
!    the easiest way to do so is to <command>SET</>
!    <literal>plperl.use_strict</literal> to true.  This parameter affects
!    subsequent compilations of <application>PL/Perl</> functions, but not
!    functions already compiled in the current session.  To set the
!    parameter before <application>PL/Perl</> has been loaded, it is
!    necessary to have added <quote><literal>plperl</></> to the <xref
!    linkend="guc-custom-variable-classes"> list in
!    <filename>postgresql.conf</filename>.
    </para>
  
    <para>
!    Another way to use the <literal>strict</> pragma is to put:
  <programlisting>
  use strict;
  </programlisting>
!    in the function body.  But this only works in <application>PL/PerlU</>
!    functions, since the <literal>use</> triggers a <literal>require</>
!    which is not a trusted operation.  In
!    <application>PL/Perl</> functions you can instead do:
! <programlisting>
! BEGIN { strict->import(); }
! </programlisting>
    </para>
   </sect1>
  
--- 285,323 ----
    </para>
  
    <para>
!    If you wish to use the <literal>strict</> pragma with your code you have a few options.
!    For temporary global use you can <command>SET</> <literal>plperl.use_strict</literal>
!    to true (see <xref linkend="plperl.use_strict">).
!    This will affect subsequent compilations of <application>PL/Perl</>
!    functions, but not functions already compiled in the current session.
!    For permanent global use you can set <literal>plperl.use_strict</literal>
!    to true in the <filename>postgresql.conf</filename> file.
    </para>
  
    <para>
!    For permanent use in specific functions you can simply put:
  <programlisting>
  use strict;
  </programlisting>
!    at the top of the function body.
!   </para>
! 
!   <para>
!   The <literal>feature</> pragma is also available to <function>use</> if your Perl is version 5.10.0 or higher.
!   </para>
! 
!  </sect1>
! 
!  <sect1 id="plperl-data">
!   <title>Data Values in PL/Perl</title>
! 
!   <para>
!    The argument values supplied to a PL/Perl function's code are
!    simply the input arguments converted to text form (just as if they
!    had been displayed by a <command>SELECT</command> statement).
!    Conversely, the <function>return</function> and <function>return_next</function>
!    commands will accept any string that is acceptable input format
!    for the function's declared return type.
    </para>
   </sect1>
  
*************** SELECT done();
*** 682,699 ****
   </sect2>
   </sect1>
  
-  <sect1 id="plperl-data">
-   <title>Data Values in PL/Perl</title>
- 
-   <para>
-    The argument values supplied to a PL/Perl function's code are
-    simply the input arguments converted to text form (just as if they
-    had been displayed by a <command>SELECT</command> statement).
-    Conversely, the <literal>return</> command will accept any string
-    that is acceptable input format for the function's declared return
-    type.  So, within the PL/Perl function,
-    all values are just text strings.
-   </para>
   </sect1>
  
   <sect1 id="plperl-global">
--- 692,697 ----
*************** CREATE TRIGGER test_valid_id_trig
*** 1042,1049 ****
     <itemizedlist>
      <listitem>
       <para>
!       PL/Perl functions cannot call each other directly (because they
!       are anonymous subroutines inside Perl).
       </para>
      </listitem>
  
--- 1040,1046 ----
     <itemizedlist>
      <listitem>
       <para>
!       PL/Perl functions cannot call each other directly.
       </para>
      </listitem>
  
*************** CREATE TRIGGER test_valid_id_trig
*** 1072,1077 ****
--- 1069,1076 ----
      </listitem>
     </itemizedlist>
    </para>
+  </sect2>
+ 
   </sect1>
  
  </chapter>
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index b942739..ebf9afd 100644
*** a/src/pl/plperl/expected/plperl.out
--- b/src/pl/plperl/expected/plperl.out
*************** $$ LANGUAGE plperl;
*** 563,568 ****
  NOTICE:  This is a test
  CONTEXT:  PL/Perl anonymous code block
  -- check that restricted operations are rejected in a plperl DO block
! DO $$ use Config; $$ LANGUAGE plperl;
! ERROR:  'require' trapped by operation mask at line 1.
  CONTEXT:  PL/Perl anonymous code block
--- 563,579 ----
  NOTICE:  This is a test
  CONTEXT:  PL/Perl anonymous code block
  -- check that restricted operations are rejected in a plperl DO block
! DO $$ eval "1+1"; $$ LANGUAGE plperl;
! ERROR:  'eval "string"' trapped by operation mask at line 1.
! CONTEXT:  PL/Perl anonymous code block
! -- check that we can't "use" a module that's not been loaded already
! -- compile-time error: "Unable to load blib.pm into plperl"
! DO $$ use blib; $$ LANGUAGE plperl;
! ERROR:  Unable to load blib.pm into plperl at line 1.
! BEGIN failed--compilation aborted at line 1.
! CONTEXT:  PL/Perl anonymous code block
! -- check that we can "use" a module that has already been loaded
! -- runtime error: "Can't use string ("foo") as a SCALAR ref while "strict refs" in use
! DO $do$ use strict; my $name = "foo"; my $ref = $$name; $do$ LANGUAGE plperl;
! ERROR:  Can't use string ("foo") as a SCALAR ref while "strict refs" in use at line 1.
  CONTEXT:  PL/Perl anonymous code block
diff --git a/src/pl/plperl/expected/plperl_plperlu.out b/src/pl/plperl/expected/plperl_plperlu.out
index 80824e0..e940f71 100644
*** a/src/pl/plperl/expected/plperl_plperlu.out
--- b/src/pl/plperl/expected/plperl_plperlu.out
***************
*** 1,18 ****
  -- test plperl/plperlu interaction
  CREATE OR REPLACE FUNCTION bar() RETURNS integer AS $$
      #die 'BANG!'; # causes server process to exit(2)
      # alternative - causes server process to exit(255)
      spi_exec_query("invalid sql statement");
! $$ language plperl; -- plperl or plperlu
     
  CREATE OR REPLACE FUNCTION foo() RETURNS integer AS $$
      spi_exec_query("SELECT * FROM bar()");
      return 1;
! $$ LANGUAGE plperlu; -- must be opposite to language of bar
     
! SELECT * FROM bar(); -- throws exception normally
  ERROR:  syntax error at or near "invalid" at line 4.
  CONTEXT:  PL/Perl function "bar"
! SELECT * FROM foo(); -- used to cause backend crash
  ERROR:  syntax error at or near "invalid" at line 4. at line 2.
  CONTEXT:  PL/Perl function "foo"
--- 1,19 ----
  -- test plperl/plperlu interaction
+ -- the language and call ordering of this test sequence is useful
  CREATE OR REPLACE FUNCTION bar() RETURNS integer AS $$
      #die 'BANG!'; # causes server process to exit(2)
      # alternative - causes server process to exit(255)
      spi_exec_query("invalid sql statement");
! $$ language plperl; -- compile plperl code
     
  CREATE OR REPLACE FUNCTION foo() RETURNS integer AS $$
      spi_exec_query("SELECT * FROM bar()");
      return 1;
! $$ LANGUAGE plperlu; -- compile plperlu code
     
! SELECT * FROM bar(); -- throws exception normally (running plperl)
  ERROR:  syntax error at or near "invalid" at line 4.
  CONTEXT:  PL/Perl function "bar"
! SELECT * FROM foo(); -- used to cause backend crash (after switching to plperlu)
  ERROR:  syntax error at or near "invalid" at line 4. at line 2.
  CONTEXT:  PL/Perl function "foo"
diff --git a/src/pl/plperl/plc_perlboot.pl b/src/pl/plperl/plc_perlboot.pl
index b4d1e04..769721d 100644
*** a/src/pl/plperl/plc_perlboot.pl
--- b/src/pl/plperl/plc_perlboot.pl
*************** sub ::plperl_die {
*** 18,34 ****
  }
  $SIG{__DIE__} = \&::plperl_die;
  
  
! sub ::mkunsafefunc {
! 	my $ret = eval(qq[ sub { $_[0] $_[1] } ]);
! 	$@ =~ s/\(eval \d+\) //g if $@;
! 	return $ret;
  }
-   
- use strict;
  
! sub ::mk_strict_unsafefunc {
! 	my $ret = eval(qq[ sub { use strict; $_[0] $_[1] } ]);
  	$@ =~ s/\(eval \d+\) //g if $@;
  	return $ret;
  }
--- 18,45 ----
  }
  $SIG{__DIE__} = \&::plperl_die;
  
+ sub ::mkfuncsrc {
+ 	my ($name, $imports, $prolog, $src) = @_;
  
! 	my $BEGIN = join "\n", map {
! 		my $names = $imports->{$_} || [];
! 		"$_->import(qw(@$names));"
! 	} keys %$imports;
! 	$BEGIN &&= "BEGIN { $BEGIN }";
! 
! 	$name =~ s/\\/\\\\/g;
! 	$name =~ s/::|'/_/g; # avoid package delimiters
! 
! 	my $funcsrc;
! 	$funcsrc .= qq[ undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog $src } ];
! 	#warn "plperl mkfuncsrc: $funcsrc\n";
! 	return $funcsrc;
  }
  
! # see also mksafefunc() in plc_safe_ok.pl
! sub ::mkunsafefunc {
! 	no strict; # default to no strict for the eval
! 	my $ret = eval(::mkfuncsrc(@_));
  	$@ =~ s/\(eval \d+\) //g if $@;
  	return $ret;
  }
*************** sub ::encode_array_constructor {
*** 61,67 ****
  		if ref $arg ne 'ARRAY';
  	my $res = join ", ", map {
  		(ref $_) ? ::encode_array_constructor($_)
! 				 : ::quote_nullable($_)
  	} @$arg;
  	return "ARRAY[$res]";
  }
--- 72,78 ----
  		if ref $arg ne 'ARRAY';
  	my $res = join ", ", map {
  		(ref $_) ? ::encode_array_constructor($_)
! 		         : ::quote_nullable($_)
  	} @$arg;
  	return "ARRAY[$res]";
  }
diff --git a/src/pl/plperl/plc_safe_bad.pl b/src/pl/plperl/plc_safe_bad.pl
index 838ccc6..36ef6ae 100644
*** a/src/pl/plperl/plc_safe_bad.pl
--- b/src/pl/plperl/plc_safe_bad.pl
***************
*** 1,15 ****
! use vars qw($PLContainer);
! 
! $PLContainer = new Safe('PLPerl');
! $PLContainer->permit_only(':default');
! $PLContainer->share(qw[&elog &ERROR]);
  
! my $msg = 'trusted Perl functions disabled - please upgrade Perl Safe module to version 2.09 or later';
! sub ::mksafefunc {
!   return $PLContainer->reval(qq[sub { elog(ERROR,'$msg') }]);
! }
  
! sub ::mk_strict_safefunc {
!   return $PLContainer->reval(qq[sub { elog(ERROR,'$msg') }]);
  }
- 
--- 1,13 ----
! # Minimal version of plc_safe_ok.pl
! # that's used if Safe is too old or doesn't load for any reason
  
! my $msg = 'trusted Perl functions disabled - please upgrade Perl Safe module';
  
! sub mksafefunc {
! 	my ($name, $pragma, $prolog, $src) = @_;
! 	# replace $src with code to generate an error
! 	$src = qq{ ::elog(::ERROR,"$msg\n") };
! 	my $ret = eval(::mkfuncsrc($name, $pragma, '', $src));
! 	$@ =~ s/\(eval \d+\) //g if $@;
! 	return $ret;
  }
diff --git a/src/pl/plperl/plc_safe_ok.pl b/src/pl/plperl/plc_safe_ok.pl
index aec5cdc..dc33dd6 100644
*** a/src/pl/plperl/plc_safe_ok.pl
--- b/src/pl/plperl/plc_safe_ok.pl
***************
*** 1,8 ****
  use vars qw($PLContainer);
  
  $PLContainer = new Safe('PLPerl');
  $PLContainer->permit_only(':default');
! $PLContainer->permit(qw[:base_math !:base_io sort time]);
  
  $PLContainer->share(qw[&elog &return_next
  	&spi_query &spi_fetchrow &spi_cursor_close &spi_exec_query
--- 1,9 ----
+ use strict;
  use vars qw($PLContainer);
  
  $PLContainer = new Safe('PLPerl');
  $PLContainer->permit_only(':default');
! $PLContainer->permit(qw[:base_math !:base_io sort time require]);
  
  $PLContainer->share(qw[&elog &return_next
  	&spi_query &spi_fetchrow &spi_cursor_close &spi_exec_query
*************** $PLContainer->share(qw[&elog &return_nex
*** 14,36 ****
  	&looks_like_number
  ]);
  
! # Load strict into the container.
! # The temporary enabling of the caller opcode here is to work around a
! # bug in perl 5.10, which unkindly changed the way its Safe.pm works, without
! # notice. It is quite safe, as caller is informational only, and in any case
! # we only enable it while we load the 'strict' module.
! $PLContainer->permit(qw[require caller]);
! $PLContainer->reval('use strict;');
! $PLContainer->deny(qw[require caller]);
  
! sub ::mksafefunc {
! 	my $ret = $PLContainer->reval(qq[sub { $_[0] $_[1] }]);
  	$@ =~ s/\(eval \d+\) //g if $@;
  	return $ret;
  }
  
! sub ::mk_strict_safefunc {
! 	my $ret = $PLContainer->reval(qq[sub { BEGIN { strict->import(); } $_[0] $_[1] }]);
! 	$@ =~ s/\(eval \d+\) //g if $@;
! 	return $ret;
  }
--- 15,38 ----
  	&looks_like_number
  ]);
  
! # Load widely useful pragmas into the container to make them available.
! # (Temporarily enable caller here as work around for bug in perl 5.10,
! # which changed the way its Safe.pm works. It is quite safe, as caller is
! # informational only.)
! $PLContainer->permit(qw[caller]);
! ::safe_eval(q{
! 	require strict;
! 	require feature if $] >= 5.010000;
! 	1;
! }) or die $@;
! $PLContainer->deny(qw[caller]);
  
! sub ::safe_eval {
! 	my $ret = $PLContainer->reval(shift);
  	$@ =~ s/\(eval \d+\) //g if $@;
  	return $ret;
  }
  
! sub ::mksafefunc {
! 	return ::safe_eval(::mkfuncsrc(@_));
  }
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 6f577f0..9277072 100644
*** a/src/pl/plperl/plperl.c
--- b/src/pl/plperl/plperl.c
*************** static InterpState interp_state = INTERP
*** 132,137 ****
--- 132,138 ----
  static PerlInterpreter *plperl_trusted_interp = NULL;
  static PerlInterpreter *plperl_untrusted_interp = NULL;
  static PerlInterpreter *plperl_held_interp = NULL;
+ static OP *(*pp_require_orig)(pTHX) = NULL;
  static bool trusted_context;
  static HTAB *plperl_proc_hash = NULL;
  static HTAB *plperl_query_hash = NULL;
*************** static HV  *plperl_spi_execute_fetch_res
*** 163,173 ****
  static SV  *newSVstring(const char *str);
  static SV **hv_store_string(HV *hv, const char *key, SV *val);
  static SV **hv_fetch_string(HV *hv, const char *key);
! static void plperl_create_sub(plperl_proc_desc *desc, char *s);
  static SV  *plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo);
  static void plperl_compile_callback(void *arg);
  static void plperl_exec_callback(void *arg);
  static void plperl_inline_callback(void *arg);
  
  /*
   * Convert an SV to char * and verify the encoding via pg_verifymbstr()
--- 164,177 ----
  static SV  *newSVstring(const char *str);
  static SV **hv_store_string(HV *hv, const char *key, SV *val);
  static SV **hv_fetch_string(HV *hv, const char *key);
! static void plperl_create_sub(plperl_proc_desc *desc, char *s, Oid fn_oid);
  static SV  *plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo);
  static void plperl_compile_callback(void *arg);
  static void plperl_exec_callback(void *arg);
  static void plperl_inline_callback(void *arg);
+ static char *strip_trailing_ws(const char *msg);
+ static OP * pp_require_safe(pTHX);
+ static int restore_context(bool);
  
  /*
   * Convert an SV to char * and verify the encoding via pg_verifymbstr()
*************** sv2text_mbverified(SV *sv)
*** 187,193 ****
  	 */
  	val = SvPV(sv, len);
  	pg_verifymbstr(val, len, false);
!     return val;
  }
  
  /*
--- 191,197 ----
  	 */
  	val = SvPV(sv, len);
  	pg_verifymbstr(val, len, false);
! 	return val;
  }
  
  /*
*************** _PG_init(void)
*** 267,280 ****
   * assign that interpreter if it is available to either the trusted or
   * untrusted interpreter. If it has already been assigned, and we need to
   * create the other interpreter, we do that if we can, or error out.
-  * We detect if it is safe to run two interpreters during the setup of the
-  * dummy interpreter.
   */
  
  
  static void
! check_interp(bool trusted)
  {
  	if (interp_state == INTERP_HELD)
  	{
  		if (trusted)
--- 271,291 ----
   * assign that interpreter if it is available to either the trusted or
   * untrusted interpreter. If it has already been assigned, and we need to
   * create the other interpreter, we do that if we can, or error out.
   */
  
  
  static void
! select_perl_context(bool trusted)
  {
+ 	/*
+ 	 * handle simple cases
+ 	 */
+ 	if (restore_context(trusted))
+ 		return;
+ 
+ 	/*
+ 	 * adopt held interp if free, else create new one if possible
+ 	 */
  	if (interp_state == INTERP_HELD)
  	{
  		if (trusted)
*************** check_interp(bool trusted)
*** 287,309 ****
  			plperl_untrusted_interp = plperl_held_interp;
  			interp_state = INTERP_UNTRUSTED;
  		}
- 		plperl_held_interp = NULL;
- 		trusted_context = trusted;
- 		if (trusted) /* done last to avoid recursion */
- 			plperl_safe_init();
- 	}
- 	else if (interp_state == INTERP_BOTH ||
- 			 (trusted && interp_state == INTERP_TRUSTED) ||
- 			 (!trusted && interp_state == INTERP_UNTRUSTED))
- 	{
- 		if (trusted_context != trusted)
- 		{
- 			if (trusted)
- 				PERL_SET_CONTEXT(plperl_trusted_interp);
- 			else
- 				PERL_SET_CONTEXT(plperl_untrusted_interp);
- 			trusted_context = trusted;
- 		}
  	}
  	else
  	{
--- 298,303 ----
*************** check_interp(bool trusted)
*** 313,344 ****
  			plperl_trusted_interp = plperl;
  		else
  			plperl_untrusted_interp = plperl;
- 		plperl_held_interp = NULL;
- 		trusted_context = trusted;
  		interp_state = INTERP_BOTH;
- 		if (trusted) /* done last to avoid recursion */
- 			plperl_safe_init();
  #else
  		elog(ERROR,
  			 "cannot allocate second Perl interpreter on this platform");
  #endif
  	}
  }
  
  /*
   * Restore previous interpreter selection, if two are active
   */
! static void
! restore_context(bool old_context)
  {
! 	if (interp_state == INTERP_BOTH && trusted_context != old_context)
  	{
! 		if (old_context)
! 			PERL_SET_CONTEXT(plperl_trusted_interp);
! 		else
! 			PERL_SET_CONTEXT(plperl_untrusted_interp);
! 		trusted_context = old_context;
  	}
  }
  
  static PerlInterpreter *
--- 307,358 ----
  			plperl_trusted_interp = plperl;
  		else
  			plperl_untrusted_interp = plperl;
  		interp_state = INTERP_BOTH;
  #else
  		elog(ERROR,
  			 "cannot allocate second Perl interpreter on this platform");
  #endif
  	}
+ 	plperl_held_interp = NULL;
+ 	trusted_context = trusted;
+ 
+ 	/*
+ 	 * initialization - done after plperl_*_interp and trusted_context
+ 	 * updates above to ensure a clean state (and thereby avoid recursion via
+ 	 * plperl_safe_init caling plperl_call_perl_func for utf8fix)
+ 	 */
+ 	if (trusted) {
+ 		plperl_safe_init();
+ 		PL_ppaddr[OP_REQUIRE] = pp_require_safe;
+ 	}
  }
  
  /*
   * Restore previous interpreter selection, if two are active
   */
! static int
! restore_context(bool trusted)
  {
! 	if (interp_state == INTERP_BOTH ||
! 		( trusted && interp_state == INTERP_TRUSTED) ||
! 		(!trusted && interp_state == INTERP_UNTRUSTED))
  	{
! 		if (trusted_context != trusted)
! 		{
! 			if (trusted) {
! 				PERL_SET_CONTEXT(plperl_trusted_interp);
! 				PL_ppaddr[OP_REQUIRE] = pp_require_safe;
! 			}
! 			else {
! 				PERL_SET_CONTEXT(plperl_untrusted_interp);
! 				PL_ppaddr[OP_REQUIRE] = pp_require_orig;
! 			}
! 			trusted_context = trusted;
! 		}
! 		return 1; /* context restored */
  	}
+ 
+ 	return 0;     /* unable - appropriate interpreter not available */
  }
  
  static PerlInterpreter *
*************** plperl_init_interp(void)
*** 422,427 ****
--- 436,451 ----
  
  	PERL_SET_CONTEXT(plperl);
  	perl_construct(plperl);
+ 
+ 	/*
+ 	 * Record the original function for the 'require' opcode.
+ 	 * Ensure it's used for new interpreters.
+ 	 */
+ 	if (!pp_require_orig)
+ 		pp_require_orig = PL_ppaddr[OP_REQUIRE];
+ 	else
+ 		PL_ppaddr[OP_REQUIRE] = pp_require_orig;
+ 
  	perl_parse(plperl, plperl_init_shared_libs,
  			   nargs, embedding, NULL);
  	perl_run(plperl);
*************** plperl_init_interp(void)
*** 471,496 ****
  }
  
  
  static void
  plperl_safe_init(void)
  {
  	SV		   *safe_version_sv;
  
  	safe_version_sv = eval_pv(SAFE_MODULE, FALSE);	/* TRUE = croak if failure */
  
  	/*
! 	 * We actually want to reject Safe version < 2.09, but it's risky to
! 	 * assume that floating-point comparisons are exact, so use a slightly
! 	 * smaller comparison value.
  	 */
! 	if (SvNV(safe_version_sv) < 2.0899)
  	{
  		/* not safe, so disallow all trusted funcs */
  		eval_pv(PLC_SAFE_BAD, FALSE);
  	}
  	else
  	{
  		eval_pv(PLC_SAFE_OK, FALSE);
  		if (GetDatabaseEncoding() == PG_UTF8)
  		{
  			/*
--- 495,565 ----
  }
  
  
+ /*
+  * Our safe implementation of the require opcode.
+  * This is safe because it's completely unable to load any code.
+  * If the requested file/module has already been loaded it'll return true.
+  * If not, it'll die.
+  * So now "use Foo;" will work iff Foo has already been loaded.
+  */
+ static OP *
+ pp_require_safe(pTHX)
+ {
+ 	dVAR; dSP;
+ 	SV *sv, **svp;
+ 	char *name;
+ 	STRLEN len;
+ 
+     sv = POPs;
+     name = SvPV(sv, len);
+     if (!(name && len > 0 && *name))
+         RETPUSHNO;
+ 
+ 	svp = hv_fetch(GvHVn(PL_incgv), name, len, 0);
+ 	if (svp && *svp != &PL_sv_undef)
+ 		RETPUSHYES;
+ 
+ 	DIE(aTHX_ "Unable to load %s into plperl", name);
+ }
+ 
+ 
  static void
  plperl_safe_init(void)
  {
  	SV		   *safe_version_sv;
+ 	IV			safe_version_x100;
  
  	safe_version_sv = eval_pv(SAFE_MODULE, FALSE);	/* TRUE = croak if failure */
+ 	safe_version_x100 = (int)(SvNV(safe_version_sv) * 100);
  
  	/*
! 	 * Reject too-old versions of Safe and some others:
! 	 * 2.20: http://rt.perl.org/rt3/Ticket/Display.html?id=72068
  	 */
! 	if (safe_version_x100 < 209 || safe_version_x100 == 220)
  	{
  		/* not safe, so disallow all trusted funcs */
  		eval_pv(PLC_SAFE_BAD, FALSE);
+ 		if (SvTRUE(ERRSV))
+ 		{
+ 			ereport(ERROR,
+ 				(errcode(ERRCODE_INTERNAL_ERROR),
+ 				 errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
+ 				 errdetail("While executing PLC_SAFE_BAD")));
+ 		}
+ 
  	}
  	else
  	{
  		eval_pv(PLC_SAFE_OK, FALSE);
+ 		if (SvTRUE(ERRSV))
+ 		{
+ 			ereport(ERROR,
+ 				(errcode(ERRCODE_INTERNAL_ERROR),
+ 				 errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
+ 				 errdetail("While executing PLC_SAFE_OK")));
+ 		}
+ 
  		if (GetDatabaseEncoding() == PG_UTF8)
  		{
  			/*
*************** plperl_safe_init(void)
*** 502,507 ****
--- 571,577 ----
  			 */
  			plperl_proc_desc desc;
  			FunctionCallInfoData fcinfo;
+ 			SV *perlret;
  
  			desc.proname = "utf8fix";
  			desc.lanpltrusted = true;
*************** plperl_safe_init(void)
*** 511,524 ****
  
  			/* compile the function */
  			plperl_create_sub(&desc,
! 					"return shift =~ /\\xa9/i ? 'true' : 'false' ;");
  
  			/* set up to call the function with a single text argument 'a' */
  			fcinfo.arg[0] = CStringGetTextDatum("a");
  			fcinfo.argnull[0] = false;
  
  			/* and make the call */
! 			(void) plperl_call_perl_func(&desc, &fcinfo);
  		}
  	}
  }
--- 581,596 ----
  
  			/* compile the function */
  			plperl_create_sub(&desc,
! 					"return shift =~ /\\xa9/i ? 'true' : 'false' ;", 0);
  
  			/* set up to call the function with a single text argument 'a' */
  			fcinfo.arg[0] = CStringGetTextDatum("a");
  			fcinfo.argnull[0] = false;
  
  			/* and make the call */
! 			perlret = plperl_call_perl_func(&desc, &fcinfo);
! 
! 			SvREFCNT_dec(perlret);
  		}
  	}
  }
*************** plperl_convert_to_pg_array(SV *src)
*** 582,588 ****
  {
  	SV		   *rv;
  	int			count;
- 
  	dSP;
  
  	PUSHMARK(SP);
--- 654,659 ----
*************** plperl_trigger_build_args(FunctionCallIn
*** 619,624 ****
--- 690,696 ----
  	HV		   *hv;
  
  	hv = newHV();
+ 	hv_ksplit(hv, 12); /* pre-grow the hash */
  
  	tdata = (TriggerData *) fcinfo->context;
  	tupdesc = tdata->tg_relation->rd_att;
*************** plperl_trigger_build_args(FunctionCallIn
*** 673,678 ****
--- 745,751 ----
  	{
  		AV		   *av = newAV();
  
+ 		av_extend(av, tdata->tg_trigger->tgnargs);
  		for (i = 0; i < tdata->tg_trigger->tgnargs; i++)
  			av_push(av, newSVstring(tdata->tg_trigger->tgargs[i]));
  		hv_store_string(hv, "args", newRV_noinc((SV *) av));
*************** plperl_inline_handler(PG_FUNCTION_ARGS)
*** 893,901 ****
  		if (SPI_connect() != SPI_OK_CONNECT)
  			elog(ERROR, "could not connect to SPI manager");
  
! 		check_interp(desc.lanpltrusted);
  
! 		plperl_create_sub(&desc, codeblock->source_text);
  
  		if (!desc.reference)	/* can this happen? */
  			elog(ERROR, "could not create internal procedure for anonymous code block");
--- 966,974 ----
  		if (SPI_connect() != SPI_OK_CONNECT)
  			elog(ERROR, "could not connect to SPI manager");
  
! 		select_perl_context(desc.lanpltrusted);
  
! 		plperl_create_sub(&desc, codeblock->source_text, 0);
  
  		if (!desc.reference)	/* can this happen? */
  			elog(ERROR, "could not create internal procedure for anonymous code block");
*************** plperl_validator(PG_FUNCTION_ARGS)
*** 1000,1022 ****
  
  
  /*
!  * Uses mksafefunc/mkunsafefunc to create an anonymous sub whose text is
!  * supplied in s, and returns a reference to the closure.
   */
  static void
! plperl_create_sub(plperl_proc_desc *prodesc, char *s)
  {
  	dSP;
  	bool        trusted = prodesc->lanpltrusted;
! 	SV		   *subref;
! 	int			count;
! 	char	   *compile_sub;
  
  	ENTER;
  	SAVETMPS;
  	PUSHMARK(SP);
! 	XPUSHs(sv_2mortal(newSVstring("our $_TD; local $_TD=$_[0]; shift;")));
! 	XPUSHs(sv_2mortal(newSVstring(s)));
  	PUTBACK;
  
  	/*
--- 1073,1105 ----
  
  
  /*
!  * Uses mksafefunc/mkunsafefunc to create a subroutine whose text is
!  * supplied in s, and returns a reference to it
   */
  static void
! plperl_create_sub(plperl_proc_desc *prodesc, char *s, Oid fn_oid)
  {
  	dSP;
  	bool        trusted = prodesc->lanpltrusted;
! 	char        subname[NAMEDATALEN+40];
! 	HV         *pragma_hv = newHV();
! 	SV         *subref = NULL;
! 	int         count;
! 	char       *compile_sub;
! 
! 	sprintf(subname, "%s__%u", prodesc->proname, fn_oid);
! 
! 	if (plperl_use_strict)
! 		hv_store_string(pragma_hv, "strict", (SV*)newAV());
  
  	ENTER;
  	SAVETMPS;
  	PUSHMARK(SP);
! 	EXTEND(SP,4);
! 	PUSHs(sv_2mortal(newSVstring(subname)));
! 	PUSHs(sv_2mortal(newRV_noinc((SV*)pragma_hv)));
! 	PUSHs(sv_2mortal(newSVstring("our $_TD; local $_TD=shift;")));
! 	PUSHs(sv_2mortal(newSVstring(s)));
  	PUTBACK;
  
  	/*
*************** plperl_create_sub(plperl_proc_desc *prod
*** 1024,1080 ****
  	 * errors properly.  Perhaps it's because there's another level of eval
  	 * inside mksafefunc?
  	 */
! 
! 	if (trusted && plperl_use_strict)
! 		compile_sub = "::mk_strict_safefunc";
! 	else if (plperl_use_strict)
! 		compile_sub = "::mk_strict_unsafefunc";
! 	else if (trusted)
! 		compile_sub = "::mksafefunc";
! 	else
! 		compile_sub = "::mkunsafefunc";
! 
  	count = perl_call_pv(compile_sub, G_SCALAR | G_EVAL | G_KEEPERR);
  	SPAGAIN;
  
! 	if (count != 1)
! 	{
! 		PUTBACK;
! 		FREETMPS;
! 		LEAVE;
! 		elog(ERROR, "didn't get a return item from mksafefunc");
  	}
  
! 	subref = POPs;
  
  	if (SvTRUE(ERRSV))
  	{
- 		PUTBACK;
- 		FREETMPS;
- 		LEAVE;
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
  				 errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV)))));
  	}
  
! 	if (!SvROK(subref) || SvTYPE(SvRV(subref)) != SVt_PVCV)
  	{
! 		PUTBACK;
! 		FREETMPS;
! 		LEAVE;
! 		elog(ERROR, "didn't get a code ref");
  	}
  
- 	/*
- 	 * need to make a copy of the return, it comes off the stack as a
- 	 * temporary.
- 	 */
  	prodesc->reference = newSVsv(subref);
  
- 	PUTBACK;
- 	FREETMPS;
- 	LEAVE;
- 
  	return;
  }
  
--- 1107,1142 ----
  	 * errors properly.  Perhaps it's because there's another level of eval
  	 * inside mksafefunc?
  	 */
! 	compile_sub = (trusted) ? "::mksafefunc" : "::mkunsafefunc";
  	count = perl_call_pv(compile_sub, G_SCALAR | G_EVAL | G_KEEPERR);
  	SPAGAIN;
  
! 	if (count == 1) {
! 		GV *sub_glob = (GV*)POPs;
! 		if (sub_glob && SvTYPE(sub_glob) == SVt_PVGV)
! 			subref = newRV_inc((SV*)GvCVu((GV*)sub_glob));
  	}
  
! 	PUTBACK;
! 	FREETMPS;
! 	LEAVE;
  
  	if (SvTRUE(ERRSV))
  	{
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
  				 errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV)))));
  	}
  
! 	if (!subref)
  	{
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INTERNAL_ERROR),
! 				 errmsg("didn't get a GLOB from compiling %s via %s", prodesc->proname, compile_sub)));
  	}
  
  	prodesc->reference = newSVsv(subref);
  
  	return;
  }
  
*************** plperl_call_perl_func(plperl_proc_desc *
*** 1118,1130 ****
  	SAVETMPS;
  
  	PUSHMARK(SP);
  
! 	XPUSHs(&PL_sv_undef);		/* no trigger data */
  
  	for (i = 0; i < desc->nargs; i++)
  	{
  		if (fcinfo->argnull[i])
! 			XPUSHs(&PL_sv_undef);
  		else if (desc->arg_is_rowtype[i])
  		{
  			HeapTupleHeader td;
--- 1180,1193 ----
  	SAVETMPS;
  
  	PUSHMARK(SP);
+ 	EXTEND(sp, 1 + desc->nargs);
  
! 	PUSHs(&PL_sv_undef);		/* no trigger data */
  
  	for (i = 0; i < desc->nargs; i++)
  	{
  		if (fcinfo->argnull[i])
! 			PUSHs(&PL_sv_undef);
  		else if (desc->arg_is_rowtype[i])
  		{
  			HeapTupleHeader td;
*************** plperl_call_perl_func(plperl_proc_desc *
*** 1144,1150 ****
  			tmptup.t_data = td;
  
  			hashref = plperl_hash_from_tuple(&tmptup, tupdesc);
! 			XPUSHs(sv_2mortal(hashref));
  			ReleaseTupleDesc(tupdesc);
  		}
  		else
--- 1207,1213 ----
  			tmptup.t_data = td;
  
  			hashref = plperl_hash_from_tuple(&tmptup, tupdesc);
! 			PUSHs(sv_2mortal(hashref));
  			ReleaseTupleDesc(tupdesc);
  		}
  		else
*************** plperl_call_perl_func(plperl_proc_desc *
*** 1154,1160 ****
  			tmp = OutputFunctionCall(&(desc->arg_out_func[i]),
  									 fcinfo->arg[i]);
  			sv = newSVstring(tmp);
! 			XPUSHs(sv_2mortal(sv));
  			pfree(tmp);
  		}
  	}
--- 1217,1223 ----
  			tmp = OutputFunctionCall(&(desc->arg_out_func[i]),
  									 fcinfo->arg[i]);
  			sv = newSVstring(tmp);
! 			PUSHs(sv_2mortal(sv));
  			pfree(tmp);
  		}
  	}
*************** plperl_func_handler(PG_FUNCTION_ARGS)
*** 1293,1299 ****
  							"cannot accept a set")));
  	}
  
! 	check_interp(prodesc->lanpltrusted);
  
  	perlret = plperl_call_perl_func(prodesc, fcinfo);
  
--- 1356,1362 ----
  							"cannot accept a set")));
  	}
  
! 	select_perl_context(prodesc->lanpltrusted);
  
  	perlret = plperl_call_perl_func(prodesc, fcinfo);
  
*************** plperl_trigger_handler(PG_FUNCTION_ARGS)
*** 1440,1446 ****
  	pl_error_context.arg = prodesc->proname;
  	error_context_stack = &pl_error_context;
  
! 	check_interp(prodesc->lanpltrusted);
  
  	svTD = plperl_trigger_build_args(fcinfo);
  	perlret = plperl_call_perl_trigger_func(prodesc, fcinfo, svTD);
--- 1503,1509 ----
  	pl_error_context.arg = prodesc->proname;
  	error_context_stack = &pl_error_context;
  
! 	select_perl_context(prodesc->lanpltrusted);
  
  	svTD = plperl_trigger_build_args(fcinfo);
  	perlret = plperl_call_perl_trigger_func(prodesc, fcinfo, svTD);
*************** compile_plperl_function(Oid fn_oid, bool
*** 1757,1765 ****
  		 * Create the procedure in the interpreter
  		 ************************************************************/
  
! 		check_interp(prodesc->lanpltrusted);
  
! 		plperl_create_sub(prodesc, proc_source);
  
  		restore_context(oldcontext);
  
--- 1820,1828 ----
  		 * Create the procedure in the interpreter
  		 ************************************************************/
  
! 		select_perl_context(prodesc->lanpltrusted);
  
! 		plperl_create_sub(prodesc, proc_source, fn_oid);
  
  		restore_context(oldcontext);
  
*************** plperl_hash_from_tuple(HeapTuple tuple, 
*** 1795,1800 ****
--- 1858,1864 ----
  	int			i;
  
  	hv = newHV();
+ 	hv_ksplit(hv, tupdesc->natts); /* pre-grow the hash */
  
  	for (i = 0; i < tupdesc->natts; i++)
  	{
*************** plperl_spi_execute_fetch_result(SPITuple
*** 1922,1927 ****
--- 1986,1992 ----
  		int			i;
  
  		rows = newAV();
+ 		av_extend(rows, processed);
  		for (i = 0; i < processed; i++)
  		{
  			row = plperl_hash_from_tuple(tuptable->vals[i], tuptable->tupdesc);
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index 08e5371..e6ef5f0 100644
*** a/src/pl/plperl/sql/plperl.sql
--- b/src/pl/plperl/sql/plperl.sql
*************** DO $$
*** 368,372 ****
  $$ LANGUAGE plperl;
  
  -- check that restricted operations are rejected in a plperl DO block
! DO $$ use Config; $$ LANGUAGE plperl;
  
--- 368,380 ----
  $$ LANGUAGE plperl;
  
  -- check that restricted operations are rejected in a plperl DO block
! DO $$ eval "1+1"; $$ LANGUAGE plperl;
! 
! -- check that we can't "use" a module that's not been loaded already
! -- compile-time error: "Unable to load blib.pm into plperl"
! DO $$ use blib; $$ LANGUAGE plperl;
! 
! -- check that we can "use" a module that has already been loaded
! -- runtime error: "Can't use string ("foo") as a SCALAR ref while "strict refs" in use
! DO $do$ use strict; my $name = "foo"; my $ref = $$name; $do$ LANGUAGE plperl;
  
diff --git a/src/pl/plperl/sql/plperl_plperlu.sql b/src/pl/plperl/sql/plperl_plperlu.sql
index 5b57a82..fc2bb7b 100644
*** a/src/pl/plperl/sql/plperl_plperlu.sql
--- b/src/pl/plperl/sql/plperl_plperlu.sql
***************
*** 1,17 ****
  -- test plperl/plperlu interaction
  
  CREATE OR REPLACE FUNCTION bar() RETURNS integer AS $$
      #die 'BANG!'; # causes server process to exit(2)
      # alternative - causes server process to exit(255)
      spi_exec_query("invalid sql statement");
! $$ language plperl; -- plperl or plperlu
     
  CREATE OR REPLACE FUNCTION foo() RETURNS integer AS $$
      spi_exec_query("SELECT * FROM bar()");
      return 1;
! $$ LANGUAGE plperlu; -- must be opposite to language of bar
     
! SELECT * FROM bar(); -- throws exception normally
! SELECT * FROM foo(); -- used to cause backend crash
  
  
--- 1,19 ----
  -- test plperl/plperlu interaction
  
+ -- the language and call ordering of this test sequence is useful
+ 
  CREATE OR REPLACE FUNCTION bar() RETURNS integer AS $$
      #die 'BANG!'; # causes server process to exit(2)
      # alternative - causes server process to exit(255)
      spi_exec_query("invalid sql statement");
! $$ language plperl; -- compile plperl code
     
  CREATE OR REPLACE FUNCTION foo() RETURNS integer AS $$
      spi_exec_query("SELECT * FROM bar()");
      return 1;
! $$ LANGUAGE plperlu; -- compile plperlu code
     
! SELECT * FROM bar(); -- throws exception normally (running plperl)
! SELECT * FROM foo(); -- used to cause backend crash (after switching to plperlu)
  
  
#2David E. Wheeler
david@kineticode.com
In reply to: Tim Bunce (#1)
Re: Miscellaneous changes to plperl [PATCH]

On Jan 14, 2010, at 8:07 AM, Tim Bunce wrote:

- Stored procedure subs are now given names.
The names are not visible in ordinary use, but they make
tools like Devel::NYTProf and Devel::Cover _much_ more useful.

Wasn't this in the previous patch, too?

Best,

David

#3Tim Bunce
Tim.Bunce@pobox.com
In reply to: David E. Wheeler (#2)
Re: Miscellaneous changes to plperl [PATCH]

On Thu, Jan 14, 2010 at 09:34:42AM -0800, David E. Wheeler wrote:

On Jan 14, 2010, at 8:07 AM, Tim Bunce wrote:

- Stored procedure subs are now given names.
The names are not visible in ordinary use, but they make
tools like Devel::NYTProf and Devel::Cover _much_ more useful.

Wasn't this in the previous patch, too?

Ah, I see it was in the description of the previous patch but not in the
patch itself. Thanks. I'll add a note to the commitfest.

Tim.

#4David Fetter
david@fetter.org
In reply to: Tim Bunce (#3)
Re: Miscellaneous changes to plperl [PATCH]

On Thu, Jan 14, 2010 at 05:49:54PM +0000, Tim Bunce wrote:

On Thu, Jan 14, 2010 at 09:34:42AM -0800, David E. Wheeler wrote:

On Jan 14, 2010, at 8:07 AM, Tim Bunce wrote:

- Stored procedure subs are now given names.
The names are not visible in ordinary use, but they make
tools like Devel::NYTProf and Devel::Cover _much_ more useful.

Wasn't this in the previous patch, too?

Ah, I see it was in the description of the previous patch but not in
the patch itself. Thanks. I'll add a note to the commitfest.

A description here would help, too :)

Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics

Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate

#5Alex Hunsaker
badalex@gmail.com
In reply to: Tim Bunce (#1)
1 attachment(s)
Re: Miscellaneous changes to plperl [PATCH]

On Thu, Jan 14, 2010 at 09:07, Tim Bunce <Tim.Bunce@pobox.com> wrote:

- Allow (ineffective) use of 'require' in plperl
   If the required module is not already loaded then it dies.
   So "use strict;" now works in plperl.

[ BTW I think this is awesome! ]

Id vote for use warnings; as well.

- Stored procedure subs are now given names.
   The names are not visible in ordinary use, but they make
   tools like Devel::NYTProf and Devel::Cover _much_ more useful.

This needs to quote at least '. Any others you can think of? Also I
think we should sort the imports in ::mkfunsort so that they are
stable.

The cleanups were nice, the code worked as described. Other than the
quoting issue it looks good to me. Find below an incremental patch
that fixes the items above.

diff --git a/src/pl/plperl/plc_perlboot.pl b/src/pl/plperl/plc_perlboot.pl
index daef469..fa5df0a 100644
--- a/src/pl/plperl/plc_perlboot.pl
+++ b/src/pl/plperl/plc_perlboot.pl
@@ -27,16 +27,14 @@ sub ::mkfuncsrc {
    my $BEGIN = join "\n", map {
        my $names = $imports->{$_} || [];
        "$_->import(qw(@$names));"
-   } keys %$imports;
+   } sort keys %$imports;
    $BEGIN &&= "BEGIN { $BEGIN }";

$name =~ s/\\/\\\\/g;
$name =~ s/::|'/_/g; # avoid package delimiters
+ $name =~ s/'/\'/g;

-   my $funcsrc;
-   $funcsrc .= qq[ undef *{'$name'}; *{'$name'} = sub { $BEGIN
$prolog $src } ];
-   #warn "plperl mkfuncsrc: $funcsrc\n";
-   return $funcsrc;
+   return qq[ undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog $src } ];
 }
 # see also mksafefunc() in plc_safe_ok.pl
diff --git a/src/pl/plperl/plc_safe_ok.pl b/src/pl/plperl/plc_safe_ok.pl
index 8d35357..79d64ce 100644
--- a/src/pl/plperl/plc_safe_ok.pl
+++ b/src/pl/plperl/plc_safe_ok.pl
@@ -25,6 +25,7 @@ $PLContainer->share(qw[&elog &return_next
 $PLContainer->permit(qw[caller]);
 ::safe_eval(q{
    require strict;
+   require warnings;
    require feature if $] >= 5.010000;
    1;
 }) or die $@;

Attachments:

plperl_misc_inc_ah.patchapplication/octet-stream; name=plperl_misc_inc_ah.patchDownload
*** a/src/pl/plperl/plc_perlboot.pl
--- b/src/pl/plperl/plc_perlboot.pl
***************
*** 27,42 **** sub ::mkfuncsrc {
  	my $BEGIN = join "\n", map {
  		my $names = $imports->{$_} || [];
  		"$_->import(qw(@$names));"
! 	} keys %$imports;
  	$BEGIN &&= "BEGIN { $BEGIN }";
  
  	$name =~ s/\\/\\\\/g;
  	$name =~ s/::|'/_/g; # avoid package delimiters
  
! 	my $funcsrc;
! 	$funcsrc .= qq[ undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog $src } ];
! 	#warn "plperl mkfuncsrc: $funcsrc\n";
! 	return $funcsrc;
  }
  
  # see also mksafefunc() in plc_safe_ok.pl
--- 27,40 ----
  	my $BEGIN = join "\n", map {
  		my $names = $imports->{$_} || [];
  		"$_->import(qw(@$names));"
! 	} sort keys %$imports;
  	$BEGIN &&= "BEGIN { $BEGIN }";
  
  	$name =~ s/\\/\\\\/g;
  	$name =~ s/::|'/_/g; # avoid package delimiters
+ 	$name =~ s/'/\'/g;
  
! 	return qq[ undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog $src } ];
  }
  
  # see also mksafefunc() in plc_safe_ok.pl
*** a/src/pl/plperl/plc_safe_ok.pl
--- b/src/pl/plperl/plc_safe_ok.pl
***************
*** 25,30 **** $PLContainer->share(qw[&elog &return_next
--- 25,31 ----
  $PLContainer->permit(qw[caller]);
  ::safe_eval(q{
  	require strict;
+ 	require warnings;
  	require feature if $] >= 5.010000;
  	1;
  }) or die $@;
#6David E. Wheeler
david@kineticode.com
In reply to: Alex Hunsaker (#5)
Re: Miscellaneous changes to plperl [PATCH]

On Jan 22, 2010, at 7:59 PM, Alex Hunsaker wrote:

$name =~ s/::|'/_/g; # avoid package delimiters
+ $name =~ s/'/\'/g;

Looks to me like ' is already handled in the line above the one you added, no?

David

#7Alex Hunsaker
badalex@gmail.com
In reply to: David E. Wheeler (#6)
Re: Miscellaneous changes to plperl [PATCH]

On Sat, Jan 23, 2010 at 11:30, David E. Wheeler <david@kineticode.com> wrote:

On Jan 22, 2010, at 7:59 PM, Alex Hunsaker wrote:

   $name =~ s/::|'/_/g; # avoid package delimiters
+   $name =~ s/'/\'/g;

Looks to me like ' is already handled in the line above the one you added, no?

Well no, i suppose we could fix that via:
$name =~ s/[:|']/_/g;

Im betting that was the intent.

#8David E. Wheeler
david@kineticode.com
In reply to: Alex Hunsaker (#7)
Re: Miscellaneous changes to plperl [PATCH]

On Jan 23, 2010, at 11:20 AM, Alex Hunsaker wrote:

Well no, i suppose we could fix that via:
$name =~ s/[:|']/_/g;

Im betting that was the intent.

Doubtful. In Perl, the package separator is either `::` or `'` (for hysterical reasons). So the original code was replacing any package separator with a single underscore. Your regex would change This::Module to This__Module, which I'm certain was not the intent.

Best,

David

#9Alex Hunsaker
badalex@gmail.com
In reply to: David E. Wheeler (#8)
Re: Miscellaneous changes to plperl [PATCH]

On Sat, Jan 23, 2010 at 12:42, David E. Wheeler <david@kineticode.com> wrote:

On Jan 23, 2010, at 11:20 AM, Alex Hunsaker wrote:

Well no, i suppose we could fix that via:
$name =~ s/[:|']/_/g;

Im betting that was the intent.

Doubtful. In Perl, the package separator is either `::` or `'` (for hysterical reasons). So the original code was replacing any package separator with a single underscore. Your regex would change This::Module to This__Module, which I'm certain was not the intent.

Haha, yep your right. I could have sworn I tested it with a function
name with a ' and it broke. But your obviously right :)

#10Tim Bunce
Tim.Bunce@pobox.com
In reply to: Alex Hunsaker (#9)
Re: Miscellaneous changes to plperl [PATCH]

On Fri, Jan 22, 2010 at 08:59:10PM -0700, Alex Hunsaker wrote:

On Thu, Jan 14, 2010 at 09:07, Tim Bunce <Tim.Bunce@pobox.com> wrote:

- Allow (ineffective) use of 'require' in plperl
� �If the required module is not already loaded then it dies.
� �So "use strict;" now works in plperl.

[ BTW I think this is awesome! ]

Thanks!

I'd vote for use warnings; as well.

I would to, but sadly it's not that simple.

warnings uses Carp and Carp uses eval { ... } and, owing to a sad bug in
perl < 5.11.4, Safe can't distinguish between eval "..." and eval {...}
http://rt.perl.org/rt3/Ticket/Display.html?id=70970
So trying to load warnings fails (at least for some versions of perl).

I have a version of my final "Package namespace and Safe init cleanup
for plperl" that works around that. I opted to post a less potentially
controversial version of that patch in the end. If you think allowing
plperl code to 'use warnings;' is important (and I'd tend to agree)
then I'll update that final patch.

- Stored procedure subs are now given names.
� �The names are not visible in ordinary use, but they make
� �tools like Devel::NYTProf and Devel::Cover _much_ more useful.

This needs to quote at least '. Any others you can think of? Also I
think we should sort the imports in ::mkfunsort so that they are
stable.

Sort for stability, yes. The quoting is fine though (I see you've come
to the same conclusion via David).

The cleanups were nice, the code worked as described.

Thanks.

Other than the quoting issue it looks good to me. Find below an
incremental patch that fixes the items above.

diff --git a/src/pl/plperl/plc_perlboot.pl b/src/pl/plperl/plc_perlboot.pl
index daef469..fa5df0a 100644
--- a/src/pl/plperl/plc_perlboot.pl
+++ b/src/pl/plperl/plc_perlboot.pl
@@ -27,16 +27,14 @@ sub ::mkfuncsrc {
my $BEGIN = join "\n", map {
my $names = $imports->{$_} || [];
"$_->import(qw(@$names));"
-   } keys %$imports;
+   } sort keys %$imports;

Ok, good.

$name =~ s/\\/\\\\/g;
$name =~ s/::|'/_/g; # avoid package delimiters
+ $name =~ s/'/\'/g;

Not needed.

-   my $funcsrc;
-   $funcsrc .= qq[ undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog $src } ];
-   #warn "plperl mkfuncsrc: $funcsrc\n";
-   return $funcsrc;
+   return qq[ undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog $src } ];
}

Ok. (I don't think that'll clash with any later patches.)

# see also mksafefunc() in plc_safe_ok.pl
diff --git a/src/pl/plperl/plc_safe_ok.pl b/src/pl/plperl/plc_safe_ok.pl
index 8d35357..79d64ce 100644
--- a/src/pl/plperl/plc_safe_ok.pl
+++ b/src/pl/plperl/plc_safe_ok.pl
@@ -25,6 +25,7 @@ $PLContainer->share(qw[&elog &return_next
$PLContainer->permit(qw[caller]);
::safe_eval(q{
require strict;
+   require warnings;
require feature if $] >= 5.010000;
1;
}) or die $@;

Not viable, sadly.

On Sat, Jan 23, 2010 at 12:42, David E. Wheeler <david@kineticode.com> wrote:

On Jan 23, 2010, at 11:20 AM, Alex Hunsaker wrote:

Well no, i suppose we could fix that via:
$name =~ s/[:|']/_/g;

Im betting that was the intent.

Doubtful. In Perl, the package separator is either `::` or `'` (for hysterical reasons). So the original code was replacing any package separator with a single underscore. Your regex would change This::Module to This__Module, which I'm certain was not the intent.

Haha, yep your right. I could have sworn I tested it with a function
name with a ' and it broke. But your obviously right :)

I could have sworn I wrote a test file with a bunch of stressful names.
All seems well though:

template1=# create or replace function "a'b*c}d!"() returns text language plperl as '42'; CREATE FUNCTION
template1=# select "a'b*c}d!"();
a'b*c}d!
----------
42

So, what now? Should I resend the patch with the two 'ok' changes above
included, or can the committer make those very minor changes?

Tim.

#11Andrew Dunstan
andrew@dunslane.net
In reply to: Tim Bunce (#10)
Re: Miscellaneous changes to plperl [PATCH]

Tim Bunce wrote:

-   } keys %$imports;
+   } sort keys %$imports;

Ok, good.

-   my $funcsrc;
-   $funcsrc .= qq[ undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog $src } ];
-   #warn "plperl mkfuncsrc: $funcsrc\n";
-   return $funcsrc;
+   return qq[ undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog $src } ];

Ok. (I don't think that'll clash with any later patches.)

So, what now? Should I resend the patch with the two 'ok' changes above
included, or can the committer make those very minor changes?

I'll pick these up, if Alex marks it ready for committer.

cheers

andrew

#12Alex Hunsaker
badalex@gmail.com
In reply to: Andrew Dunstan (#11)
Re: Miscellaneous changes to plperl [PATCH]

On Sat, Jan 23, 2010 at 16:26, Andrew Dunstan <andrew@dunslane.net> wrote:

Tim Bunce wrote:

-   } keys %$imports;
+   } sort keys %$imports;

Ok, good.

-   my $funcsrc;
-   $funcsrc .= qq[ undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog
$src } ];
-   #warn "plperl mkfuncsrc: $funcsrc\n";
-   return $funcsrc;
+   return qq[ undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog $src }
];

Ok. (I don't think that'll clash with any later patches.)

So, what now? Should I resend the patch with the two 'ok' changes above
included, or can the committer make those very minor changes?

I'll pick these up, if Alex marks it ready for committer.

Done.

#13Alex Hunsaker
badalex@gmail.com
In reply to: Tim Bunce (#10)
Re: Miscellaneous changes to plperl [PATCH]

On Sat, Jan 23, 2010 at 16:16, Tim Bunce <Tim.Bunce@pobox.com> wrote:

On Fri, Jan 22, 2010 at 08:59:10PM -0700, Alex Hunsaker wrote:

On Thu, Jan 14, 2010 at 09:07, Tim Bunce <Tim.Bunce@pobox.com> wrote:
I'd vote for use warnings; as well.

I would to, but sadly it's not that simple.

warnings uses Carp and Carp uses eval { ... } and, owing to a sad bug in
perl < 5.11.4, Safe can't distinguish between eval "..." and eval {...}
http://rt.perl.org/rt3/Ticket/Display.html?id=70970
So trying to load warnings fails (at least for some versions of perl).

Well that stinks.

I have a version of my final "Package namespace and Safe init cleanup
for plperl" that works around that. I opted to post a less potentially
controversial version of that patch in the end. If you think allowing
plperl code to 'use warnings;' is important (and I'd tend to agree)
then I'll update that final patch.

Sounds good.

#14Tim Bunce
Tim.Bunce@pobox.com
In reply to: Alex Hunsaker (#13)
Re: Miscellaneous changes to plperl [PATCH]

On Sat, Jan 23, 2010 at 06:40:03PM -0700, Alex Hunsaker wrote:

On Sat, Jan 23, 2010 at 16:16, Tim Bunce <Tim.Bunce@pobox.com> wrote:

On Fri, Jan 22, 2010 at 08:59:10PM -0700, Alex Hunsaker wrote:

On Thu, Jan 14, 2010 at 09:07, Tim Bunce <Tim.Bunce@pobox.com> wrote:
I'd vote for use warnings; as well.

I would to, but sadly it's not that simple.

warnings uses Carp and Carp uses eval { ... } and, owing to a sad bug in
perl < 5.11.4, Safe can't distinguish between eval "..." and eval {...}
http://rt.perl.org/rt3/Ticket/Display.html?id=70970
So trying to load warnings fails (at least for some versions of perl).

Well that stinks.

Yeap. I was amazed that no one had run into it before.

I have a version of my final "Package namespace and Safe init cleanup
for plperl" that works around that. I opted to post a less potentially
controversial version of that patch in the end. If you think allowing
plperl code to 'use warnings;' is important (and I'd tend to agree)
then I'll update that final patch.

Sounds good.

FYI I've an updated patch ready but I'll wait till the commitfest has
got 'closer' as there's a fair chance a further update will be needed
anyway to make a patch that applies cleanly.

Tim.

#15Andrew Dunstan
andrew@dunslane.net
In reply to: Tim Bunce (#14)
Re: Miscellaneous changes to plperl [PATCH]

Tim Bunce wrote:

FYI I've an updated patch ready but I'll wait till the commitfest has
got 'closer' as there's a fair chance a further update will be needed
anyway to make a patch that applies cleanly.

I want to deal with this today or tomorrow, so don't sit on it, please.

cheers

andrew

#16Tim Bunce
Tim.Bunce@pobox.com
In reply to: Andrew Dunstan (#15)
Re: Miscellaneous changes to plperl [PATCH]

On Mon, Jan 25, 2010 at 11:09:12AM -0500, Andrew Dunstan wrote:

Tim Bunce wrote:

FYI I've an updated patch ready but I'll wait till the commitfest has
got 'closer' as there's a fair chance a further update will be needed
anyway to make a patch that applies cleanly.

I want to deal with this today or tomorrow, so don't sit on it, please.

Okay. I'll post it as a reply to the original and add it to the commitfest.

Tim.