Re: Initial refactoring of plperl.c [PATCH] - Mailing list pgsql-hackers
From | Tim Bunce |
---|---|
Subject | Re: Initial refactoring of plperl.c [PATCH] |
Date | |
Msg-id | 20091221105226.GD15262@timac.local Whole thread Raw |
In response to | Initial refactoring of plperl.c [PATCH] (Tim Bunce <Tim.Bunce@pobox.com>) |
List | pgsql-hackers |
I've submitted this patch to the open CommitFest https://commitfest.postgresql.org/action/patch_view?id=245 Tim. On Wed, Nov 25, 2009 at 03:36:25PM +0000, Tim Bunce wrote: > Following on from my earlier draft plperl.c refactoring patch, here's a > new version that's complete (from my perspective at least). > > I've started work on the enhancements to plperl I outlined on pg-general > (in the "Wishlist of PL/Perl Enhancements for 8.5" thread). > I have a working implementation of those changes, plus some performance > enhancements, that I'm now re-working into a clean set of tested and > polished patches. > > This patch is a first step that doesn't add any extra functionality. > It refactors the internals to make adding the extra functionality > easier (and more clearly visible). > > Changes in this patch: > > - Changed MULTIPLICITY check from runtime to compiletime. > No loads the large Config module. > - Changed plperl_init_interp() to return new interp > and not alter the global interp_state > - Moved plperl_safe_init() call into check_interp(). > - Removed plperl_safe_init_done state variable > as interp_state now covers that role. > - Changed plperl_create_sub() to take a plperl_proc_desc argument. > - Simplified return value handling in plperl_create_sub. > - Added a test for the effect of the utf8fix function. > - Changed perl.com link in the docs to perl.org and tweaked > wording to clarify that require, not use, is what's blocked. > - Moved perl code in large multi-line C string literal macros > out to plc_*.pl files. > - Added a test2macro.pl utility to convert the plc_*.pl files to > macros in a perlchunks.h file which is #included > > I'd appreciate any feedback on the patch. > > Tim. > > diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml > index 49631f2..4c26561 100644 > *** a/doc/src/sgml/plperl.sgml > --- b/doc/src/sgml/plperl.sgml > *************** > *** 14,20 **** > <para> > PL/Perl is a loadable procedural language that enables you to write > <productname>PostgreSQL</productname> functions in the > ! <ulink url="http://www.perl.com">Perl programming language</ulink>. > </para> > > <para> > --- 14,20 ---- > <para> > PL/Perl is a loadable procedural language that enables you to write > <productname>PostgreSQL</productname> functions in the > ! <ulink url="http://www.perl.org">Perl programming language</ulink>. > </para> > > <para> > *************** SELECT * FROM perl_set(); > *** 298,304 **** > use strict; > </programlisting> > in the function body. But this only works in <application>PL/PerlU</> > ! functions, since <literal>use</> is not a trusted operation. In > <application>PL/Perl</> functions you can instead do: > <programlisting> > BEGIN { strict->import(); } > --- 298,305 ---- > 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(); } > diff --git a/src/pl/plperl/GNUmakefile b/src/pl/plperl/GNUmakefile > index a3c3495..8989b14 100644 > *** a/src/pl/plperl/GNUmakefile > --- b/src/pl/plperl/GNUmakefile > *************** PSQLDIR = $(bindir) > *** 45,50 **** > --- 45,55 ---- > > include $(top_srcdir)/src/Makefile.shlib > > + plperl.o: perlchunks.h > + > + perlchunks.h: plc_*.pl > + $(PERL) text2macro.pl --strip='^(\#.*|\s*)$$' plc_*.pl > perlchunks.htmp > + mv perlchunks.htmp perlchunks.h > > all: all-lib > > *************** submake: > *** 65,71 **** > $(MAKE) -C $(top_builddir)/src/test/regress pg_regress$(X) > > clean distclean maintainer-clean: clean-lib > ! rm -f SPI.c $(OBJS) > rm -rf results > rm -f regression.diffs regression.out > > --- 70,76 ---- > $(MAKE) -C $(top_builddir)/src/test/regress pg_regress$(X) > > clean distclean maintainer-clean: clean-lib > ! rm -f SPI.c $(OBJS) perlchunks.htmp perlchunks.h > rm -rf results > rm -f regression.diffs regression.out > > diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out > index c8a8fdb..e9f5324 100644 > *** a/src/pl/plperl/expected/plperl.out > --- b/src/pl/plperl/expected/plperl.out > *************** $$ LANGUAGE plperl; > *** 555,557 **** > --- 555,564 ---- > SELECT perl_spi_prepared_bad(4.35) as "double precision"; > ERROR: type "does_not_exist" does not exist at line 2. > CONTEXT: PL/Perl function "perl_spi_prepared_bad" > + -- > + -- Test compilation of unicode regex > + -- > + CREATE OR REPLACE FUNCTION perl_unicode_regex(text) RETURNS INTEGER AS $$ > + # see http://rt.perl.org/rt3/Ticket/Display.html?id=47576 > + return ($_[0] =~ /\x{263A}|happy/i) ? 1 : 0; # unicode smiley > + $$ LANGUAGE plperl; > diff --git a/src/pl/plperl/plc_perlboot.pl b/src/pl/plperl/plc_perlboot.pl > index ...f4739df . > *** a/src/pl/plperl/plc_perlboot.pl > --- b/src/pl/plperl/plc_perlboot.pl > *************** > *** 0 **** > --- 1,50 ---- > + SPI::bootstrap(); > + use vars qw(%_SHARED); > + > + sub ::plperl_warn { > + (my $msg = shift) =~ s/\(eval \d+\) //g; > + &elog(&NOTICE, $msg); > + } > + $SIG{__WARN__} = \&::plperl_warn; > + > + sub ::plperl_die { > + (my $msg = shift) =~ s/\(eval \d+\) //g; > + die $msg; > + } > + $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; > + } > + > + sub ::_plperl_to_pg_array { > + my $arg = shift; > + ref $arg eq 'ARRAY' || return $arg; > + my $res = ''; > + my $first = 1; > + foreach my $elem (@$arg) { > + $res .= ', ' unless $first; $first = undef; > + if (ref $elem) { > + $res .= _plperl_to_pg_array($elem); > + } > + elsif (defined($elem)) { > + my $str = qq($elem); > + $str =~ s/([\"\\])/\\$1/g; > + $res .= qq(\"$str\"); > + } > + else { > + $res .= 'NULL' ; > + } > + } > + return qq({$res}); > + } > diff --git a/src/pl/plperl/plc_safe_bad.pl b/src/pl/plperl/plc_safe_bad.pl > index ...838ccc6 . > *** a/src/pl/plperl/plc_safe_bad.pl > --- b/src/pl/plperl/plc_safe_bad.pl > *************** > *** 0 **** > --- 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') }]); > + } > + > diff --git a/src/pl/plperl/plc_safe_ok.pl b/src/pl/plperl/plc_safe_ok.pl > index ...15c6297 . > *** a/src/pl/plperl/plc_safe_ok.pl > --- b/src/pl/plperl/plc_safe_ok.pl > *************** > *** 0 **** > --- 1,33 ---- > + 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 > + &spi_prepare &spi_exec_prepared &spi_query_prepared &spi_freeplan > + &_plperl_to_pg_array > + &DEBUG &LOG &INFO &NOTICE &WARNING &ERROR %_SHARED > + ]); > + > + # 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; > + } > diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c > index 4ed4f59..c6037d8 100644 > *** a/src/pl/plperl/plperl.c > --- b/src/pl/plperl/plperl.c > *************** > *** 43,48 **** > --- 43,51 ---- > /* perl stuff */ > #include "plperl.h" > > + /* string literal macros defining chunks of perl code */ > + #include "perlchunks.h" > + > PG_MODULE_MAGIC; > > /********************************************************************** > *************** typedef enum > *** 125,133 **** > } InterpState; > > static InterpState interp_state = INTERP_NONE; > - static bool can_run_two = false; > > - static bool plperl_safe_init_done = false; > static PerlInterpreter *plperl_trusted_interp = NULL; > static PerlInterpreter *plperl_untrusted_interp = NULL; > static PerlInterpreter *plperl_held_interp = NULL; > --- 128,134 ---- > *************** Datum plperl_call_handler(PG_FUNCTION_A > *** 147,153 **** > Datum plperl_validator(PG_FUNCTION_ARGS); > void _PG_init(void); > > ! static void plperl_init_interp(void); > > static Datum plperl_func_handler(PG_FUNCTION_ARGS); > static Datum plperl_trigger_handler(PG_FUNCTION_ARGS); > --- 148,154 ---- > Datum plperl_validator(PG_FUNCTION_ARGS); > void _PG_init(void); > > ! static PerlInterpreter *plperl_init_interp(void); > > static Datum plperl_func_handler(PG_FUNCTION_ARGS); > static Datum plperl_trigger_handler(PG_FUNCTION_ARGS); > *************** static plperl_proc_desc *compile_plperl_ > *** 156,166 **** > > static SV *plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc); > static void plperl_init_shared_libs(pTHX); > static HV *plperl_spi_execute_fetch_result(SPITupleTable *, int, int); > 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 SV *plperl_create_sub(char *proname, char *s, bool trusted); > 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); > --- 157,168 ---- > > static SV *plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc); > static void plperl_init_shared_libs(pTHX); > + static void plperl_safe_init(void); > static HV *plperl_spi_execute_fetch_result(SPITupleTable *, int, int); > 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); > *************** _PG_init(void) > *** 226,323 **** > &hash_ctl, > HASH_ELEM); > > ! plperl_init_interp(); > > inited = true; > } > > - /* Each of these macros must represent a single string literal */ > - > - #define PERLBOOT \ > - "SPI::bootstrap(); use vars qw(%_SHARED);" \ > - "sub ::plperl_warn { my $msg = shift; " \ > - " $msg =~ s/\\(eval \\d+\\) //g; &elog(&NOTICE, $msg); } " \ > - "$SIG{__WARN__} = \\&::plperl_warn; " \ > - "sub ::plperl_die { my $msg = shift; " \ > - " $msg =~ s/\\(eval \\d+\\) //g; die $msg; } " \ > - "$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; } " \ > - "sub ::_plperl_to_pg_array {" \ > - " my $arg = shift; ref $arg eq 'ARRAY' || return $arg; " \ > - " my $res = ''; my $first = 1; " \ > - " foreach my $elem (@$arg) " \ > - " { " \ > - " $res .= ', ' unless $first; $first = undef; " \ > - " if (ref $elem) " \ > - " { " \ > - " $res .= _plperl_to_pg_array($elem); " \ > - " } " \ > - " elsif (defined($elem)) " \ > - " { " \ > - " my $str = qq($elem); " \ > - " $str =~ s/([\"\\\\])/\\\\$1/g; " \ > - " $res .= qq(\"$str\"); " \ > - " } " \ > - " else " \ > - " { "\ > - " $res .= 'NULL' ; " \ > - " } "\ > - " } " \ > - " return qq({$res}); " \ > - "} " > - > #define SAFE_MODULE \ > "require Safe; $Safe::VERSION" > > - /* > - * 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. > - */ > - > - #define SAFE_OK \ > - "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 &spi_exec_query &return_next " \ > - "&spi_query &spi_fetchrow &spi_cursor_close " \ > - "&spi_prepare &spi_exec_prepared &spi_query_prepared &spi_freeplan " \ > - "&_plperl_to_pg_array " \ > - "&DEBUG &LOG &INFO &NOTICE &WARNING &ERROR %_SHARED ]);" \ > - "sub ::mksafefunc {" \ > - " my $ret = $PLContainer->reval(qq[sub { $_[0] $_[1] }]); " \ > - " $@ =~ s/\\(eval \\d+\\) //g if $@; return $ret; }" \ > - "$PLContainer->permit(qw[require caller]); $PLContainer->reval('use strict;');" \ > - "$PLContainer->deny(qw[require caller]); " \ > - "sub ::mk_strict_safefunc {" \ > - " my $ret = $PLContainer->reval(qq[sub { BEGIN { strict->import(); } $_[0] $_[1] }]); " \ > - " $@ =~ s/\\(eval \\d+\\) //g if $@; return $ret; }" > - > - #define SAFE_BAD \ > - "use vars qw($PLContainer); $PLContainer = new Safe('PLPerl');" \ > - "$PLContainer->permit_only(':default');" \ > - "$PLContainer->share(qw[&elog &ERROR ]);" \ > - "sub ::mksafefunc { return $PLContainer->reval(qq[sub { " \ > - " elog(ERROR,'trusted Perl functions disabled - " \ > - " please upgrade Perl Safe module to version 2.09 or later');}]); }" \ > - "sub ::mk_strict_safefunc { return $PLContainer->reval(qq[sub { " \ > - " elog(ERROR,'trusted Perl functions disabled - " \ > - " please upgrade Perl Safe module to version 2.09 or later');}]); }" > - > - #define TEST_FOR_MULTI \ > - "use Config; " \ > - "$Config{usemultiplicity} eq 'define' or " \ > - "($Config{usethreads} eq 'define' " \ > - " and $Config{useithreads} eq 'define')" > - > - > /******************************************************************** > * > * We start out by creating a "held" interpreter that we can use in > --- 228,242 ---- > &hash_ctl, > HASH_ELEM); > > ! plperl_held_interp = plperl_init_interp(); > ! interp_state = INTERP_HELD; > > inited = true; > } > > #define SAFE_MODULE \ > "require Safe; $Safe::VERSION" > > /******************************************************************** > * > * We start out by creating a "held" interpreter that we can use in > *************** check_interp(bool trusted) > *** 347,352 **** > --- 266,273 ---- > } > 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) || > *************** check_interp(bool trusted) > *** 361,382 **** > trusted_context = trusted; > } > } > ! else if (can_run_two) > { > ! PERL_SET_CONTEXT(plperl_held_interp); > ! plperl_init_interp(); > if (trusted) > ! plperl_trusted_interp = plperl_held_interp; > else > ! plperl_untrusted_interp = plperl_held_interp; > ! interp_state = INTERP_BOTH; > plperl_held_interp = NULL; > trusted_context = trusted; > ! } > ! else > ! { > elog(ERROR, > "cannot allocate second Perl interpreter on this platform"); > } > } > > --- 282,304 ---- > trusted_context = trusted; > } > } > ! else > { > ! #ifdef MULTIPLICITY > ! PerlInterpreter *plperl = plperl_init_interp(); > if (trusted) > ! 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_context(bool old_context) > *** 396,406 **** > } > } > > ! static void > plperl_init_interp(void) > { > static char *embedding[3] = { > ! "", "-e", PERLBOOT > }; > int nargs = 3; > > --- 318,331 ---- > } > } > > ! static PerlInterpreter * > plperl_init_interp(void) > { > + PerlInterpreter *plperl; > + static int perl_sys_init_done; > + > static char *embedding[3] = { > ! "", "-e", PLC_PERLBOOT > }; > int nargs = 3; > > *************** plperl_init_interp(void) > *** 457,487 **** > */ > #if defined(PERL_SYS_INIT3) && !defined(MYMALLOC) > /* only call this the first time through, as per perlembed man page */ > ! if (interp_state == INTERP_NONE) > { > char *dummy_env[1] = {NULL}; > > PERL_SYS_INIT3(&nargs, (char ***) &embedding, (char ***) &dummy_env); > } > #endif > > ! plperl_held_interp = perl_alloc(); > ! if (!plperl_held_interp) > elog(ERROR, "could not allocate Perl interpreter"); > > ! perl_construct(plperl_held_interp); > ! perl_parse(plperl_held_interp, plperl_init_shared_libs, > nargs, embedding, NULL); > ! perl_run(plperl_held_interp); > ! > ! if (interp_state == INTERP_NONE) > ! { > ! SV *res; > ! > ! res = eval_pv(TEST_FOR_MULTI, TRUE); > ! can_run_two = SvIV(res); > ! interp_state = INTERP_HELD; > ! } > > #ifdef WIN32 > > --- 382,405 ---- > */ > #if defined(PERL_SYS_INIT3) && !defined(MYMALLOC) > /* only call this the first time through, as per perlembed man page */ > ! if (!perl_sys_init_done) > { > char *dummy_env[1] = {NULL}; > > PERL_SYS_INIT3(&nargs, (char ***) &embedding, (char ***) &dummy_env); > + perl_sys_init_done = 1; > } > #endif > > ! plperl = perl_alloc(); > ! if (!plperl) > elog(ERROR, "could not allocate Perl interpreter"); > > ! PERL_SET_CONTEXT(plperl); > ! perl_construct(plperl); > ! perl_parse(plperl, plperl_init_shared_libs, > nargs, embedding, NULL); > ! perl_run(plperl); > > #ifdef WIN32 > > *************** plperl_init_interp(void) > *** 524,529 **** > --- 442,448 ---- > } > #endif > > + return plperl; > } > > > *************** plperl_safe_init(void) > *** 545,555 **** > if (safe_version < 2.0899) > { > /* not safe, so disallow all trusted funcs */ > ! eval_pv(SAFE_BAD, FALSE); > } > else > { > ! eval_pv(SAFE_OK, FALSE); > if (GetDatabaseEncoding() == PG_UTF8) > { > /* > --- 464,474 ---- > if (safe_version < 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) > { > /* > *************** plperl_safe_init(void) > *** 557,591 **** > * the safe container and call it. For some reason not entirely > * clear, it prevents errors that can arise from the regex code > * later trying to load utf8 modules. > */ > plperl_proc_desc desc; > FunctionCallInfoData fcinfo; > - SV *ret; > - SV *func; > - > - /* make sure we don't call ourselves recursively */ > - plperl_safe_init_done = true; > > ! /* compile the function */ > ! func = plperl_create_sub("utf8fix", > ! "return shift =~ /\\xa9/i ? 'true' : 'false' ;", > ! true); > ! > ! /* set up to call the function with a single text argument 'a' */ > ! desc.reference = func; > desc.nargs = 1; > desc.arg_is_rowtype[0] = false; > fmgr_info(F_TEXTOUT, &(desc.arg_out_func[0])); > > fcinfo.arg[0] = CStringGetTextDatum("a"); > fcinfo.argnull[0] = false; > > /* and make the call */ > ! ret = plperl_call_perl_func(&desc, &fcinfo); > } > } > - > - plperl_safe_init_done = true; > } > > /* > --- 476,504 ---- > * the safe container and call it. For some reason not entirely > * clear, it prevents errors that can arise from the regex code > * later trying to load utf8 modules. > + * See http://rt.perl.org/rt3/Ticket/Display.html?id=47576 > */ > plperl_proc_desc desc; > FunctionCallInfoData fcinfo; > > ! desc.proname = "utf8fix"; > ! desc.lanpltrusted = true; > desc.nargs = 1; > desc.arg_is_rowtype[0] = false; > fmgr_info(F_TEXTOUT, &(desc.arg_out_func[0])); > > + /* 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); > } > } > } > > /* > *************** plperl_validator(PG_FUNCTION_ARGS) > *** 970,989 **** > * Uses mksafefunc/mkunsafefunc to create an anonymous sub whose text is > * supplied in s, and returns a reference to the closure. > */ > ! static SV * > ! plperl_create_sub(char *proname, char *s, bool trusted) > { > dSP; > SV *subref; > int count; > char *compile_sub; > > - if (trusted && !plperl_safe_init_done) > - { > - plperl_safe_init(); > - SPAGAIN; > - } > - > ENTER; > SAVETMPS; > PUSHMARK(SP); > --- 883,897 ---- > * 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); > *************** plperl_create_sub(char *proname, char *s > *** 1017,1025 **** > elog(ERROR, "didn't get a return item from mksafefunc"); > } > > if (SvTRUE(ERRSV)) > { > - (void) POPs; > PUTBACK; > FREETMPS; > LEAVE; > --- 925,934 ---- > elog(ERROR, "didn't get a return item from mksafefunc"); > } > > + subref = POPs; > + > if (SvTRUE(ERRSV)) > { > PUTBACK; > FREETMPS; > LEAVE; > *************** plperl_create_sub(char *proname, char *s > *** 1028,1057 **** > errmsg("%s", strip_trailing_ws(SvPV(ERRSV, PL_na))))); > } > > - /* > - * need to make a deep copy of the return. it comes off the stack as a > - * temporary. > - */ > - subref = newSVsv(POPs); > - > if (!SvROK(subref) || SvTYPE(SvRV(subref)) != SVt_PVCV) > { > PUTBACK; > FREETMPS; > LEAVE; > - > - /* > - * subref is our responsibility because it is not mortal > - */ > - SvREFCNT_dec(subref); > elog(ERROR, "didn't get a code ref"); > } > > PUTBACK; > FREETMPS; > LEAVE; > > ! return subref; > } > > > --- 937,961 ---- > errmsg("%s", strip_trailing_ws(SvPV(ERRSV, PL_na))))); > } > > 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; > } > > > *************** compile_plperl_function(Oid fn_oid, bool > *** 1731,1739 **** > > check_interp(prodesc->lanpltrusted); > > ! prodesc->reference = plperl_create_sub(prodesc->proname, > ! proc_source, > ! prodesc->lanpltrusted); > > restore_context(oldcontext); > > --- 1635,1641 ---- > > check_interp(prodesc->lanpltrusted); > > ! plperl_create_sub(prodesc, proc_source); > > restore_context(oldcontext); > > diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql > index df17834..292b1c0 100644 > *** a/src/pl/plperl/sql/plperl.sql > --- b/src/pl/plperl/sql/plperl.sql > *************** CREATE OR REPLACE FUNCTION perl_spi_prep > *** 361,363 **** > --- 361,370 ---- > $$ LANGUAGE plperl; > SELECT perl_spi_prepared_bad(4.35) as "double precision"; > > + -- > + -- Test compilation of unicode regex > + -- > + CREATE OR REPLACE FUNCTION perl_unicode_regex(text) RETURNS INTEGER AS $$ > + # see http://rt.perl.org/rt3/Ticket/Display.html?id=47576 > + return ($_[0] =~ /\x{263A}|happy/i) ? 1 : 0; # unicode smiley > + $$ LANGUAGE plperl; > diff --git a/src/pl/plperl/text2macro.pl b/src/pl/plperl/text2macro.pl > index ...1628e86 . > *** a/src/pl/plperl/text2macro.pl > --- b/src/pl/plperl/text2macro.pl > *************** > *** 0 **** > --- 1,98 ---- > + =head1 NAME > + > + text2macro.pl - convert text files into C string-literal macro definitions > + > + =head1 SYNOPSIS > + > + text2macro [options] file ... > output.h > + > + Options: > + > + --prefix=S - add prefix S to the names of the macros > + --name=S - use S as the macro name (assumes only one file) > + --strip=S - don't include lines that match perl regex S > + > + =head1 DESCRIPTION > + > + Reads one or more text files and outputs a corresponding series of C > + pre-processor macro definitions. Each macro defines a string literal that > + contains the contents of the corresponding text file. The basename of the text > + file as capitalized and used as the name of the macro, along with an optional prefix. > + > + =cut > + > + use strict; > + use warnings; > + > + use Getopt::Long; > + > + GetOptions( > + 'prefix=s' => \my $opt_prefix, > + 'name=s' => \my $opt_name, > + 'strip=s' => \my $opt_strip, > + 'selftest!' => sub { exit selftest() }, > + ) or exit 1; > + > + die "No text files specified" > + unless @ARGV; > + > + print qq{ > + /* > + * DO NOT EDIT - THIS FILE IS AUTOGENERATED - CHANGES WILL BE LOST > + * Written by $0 from @ARGV > + */ > + }; > + > + for my $src_file (@ARGV) { > + > + (my $macro = $src_file) =~ s/ .*? (\w+) (?:\.\w+) $/$1/x; > + > + open my $src_fh, $src_file # not 3-arg form > + or die "Can't open $src_file: $!"; > + > + printf qq{#define %s%s \\\n}, > + $opt_prefix || '', > + ($opt_name) ? $opt_name : uc $macro; > + while (<$src_fh>) { > + chomp; > + > + next if $opt_strip and m/$opt_strip/o; > + > + # escape the text to suite C string literal rules > + s/\\/\\\\/g; > + s/"/\\"/g; > + > + printf qq{"%s\\n" \\\n}, $_; > + } > + print qq{""\n\n}; > + } > + > + print "/* end */\n"; > + > + exit 0; > + > + > + sub selftest { > + my $tmp = "text2macro_tmp"; > + my $string = q{a '' '\\'' "" "\\"" "\\\\" "\\\\n" b}; > + > + open my $fh, ">$tmp.pl" or die; > + print $fh $string; > + close $fh; > + > + system("perl $0 --name=X $tmp.pl > $tmp.c") == 0 or die; > + open $fh, ">>$tmp.c"; > + print $fh "#include <stdio.h>\n"; > + print $fh "int main() { puts(X); return 0; }\n"; > + close $fh; > + system("cat -n $tmp.c"); > + > + system("make $tmp") == 0 or die; > + open $fh, "./$tmp |" or die; > + my $result = <$fh>; > + unlink <$tmp.*>; > + > + warn "Test string: $string\n"; > + warn "Result : $result"; > + die "Failed!" if $result ne "$string\n"; > + } > > -- > Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) > To make changes to your subscription: > http://www.postgresql.org/mailpref/pgsql-hackers
pgsql-hackers by date: