pl/perl thoughts

Started by Andrew Dunstanalmost 22 years ago2 messages
#1Andrew Dunstan
andrew@dunslane.net

Hackers,

FYI here are some of my thoughts I just sent to Joshua Drake.

I would add a couple of points:
. since there is a security advisory for Safe.pm prior to version 2.08,
maybe we should replace "require Safe;" with "use Safe 2.08;".
. I thought about some sort of precompilation methods for perl functions
known to the database (store the function refs in a hash so you could
get at them via syntax like
&{$func_hash{schema_name}->{func_name}}(@func_args) ). I decided to
abandon this approach mainly because of postgresql's function
overloading, making disambiguation very hard. With a full SPI interface
there will be access to any function, not just plperl functions, and
postgres will do the disambiguation for you. Also, see below for a
slightly more klunky but much more lightweight approach which would mean
there was no necessity for a callback to postgresql.

cheers

andrew

Show quoted text

I haven't got past the thinking stage yet, and a few things have held
up my progress elsewhere. The one possibly productive thought I have
to share with you is this: it is probably overkill to have a separate
Safe container for each plperl function. I don't see any reason that
they shouldn't all live in one Safe container. Then they could share
data and indeed some preloaded functions without doing anything special.

Thus the following perl contained in plperl.c and executed on
interpreter startup:

require Safe; SPI::bootstrap();
sub ::mksafefunc { my $x = new Safe;
$x->permit_only(':default');$x->permit(':base_math');
$x->share(qw[&elog &DEBUG &LOG &INFO &NOTICE &WARNING
&ERROR]);
return $x->reval(qq[sub { $_[0] }]); }
sub ::mkunsafefunc {return eval(qq[ sub { $_[0] } ]); }

would become something like:

require Safe; SPI::bootstrap();
use vars qw($PLContainer); $PLContainer = new
Safe("PLPerl");

$PLContainer->permit_only(':default');$PLContainer->permit(':base_math');
$PLContainer->share(qw[&elog &DEBUG &LOG &INFO &NOTICE
&WARNING &ERROR]);
sub ::mksafefunc { return $PLContainer->reval(qq[sub {
$_[0] }]); }
sub ::mkunsafefunc {return eval(qq[ sub { $_[0] } ]); }

Now you could do something like this:

create function myplperlfuncs() returns int language plperl is '
$datavar = "foo";
$funcvar = sub { return "bar"; };
return 1;
';

create function f1 () returns text language plperl as '
return $datavar;
';

create function f2() returns text language plperl as '
return &$funcvar();
';

At the start of your session you would issue "select myplperlfuncs();"
to preload the values, and thereafter you could call f1() and f2()
quite happily.

It's actually a bit of a pity that we don't have provision for a per
database exec on startup procedure, which could handle this more
elegantly (i.e. we would register myplperlfuncs() as something to be
run on startup, so the user wouldn't have to worry at all about it.)

I have not tested any of this - it is still straight out of my head.

As others have noted, the biggest need is for full SPI access. I don't
think this is hard - just a fair bit of work. After that it would be
very nice to have a DBI type handle so that programmers used to doing
things the DBI way (and what perl programmer isn't?) will feel right
at home with plperl. In most cases I guess that would be a thin layer
over the SPI stuff. Alternatively, you could hide the SPI access and
just make the DBI handle visible - it would still be mostly calling
SPI under the hood, of course. As they say in the perl world,
TIMTOWTDI. Providing a DBI handle would also square with what happens
in the Java world, where you can write server side methods that access
the database via a JDBC interface.

#2Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#1)
Re: pl/perl thoughts

FWIW, I have now tested the scheme below, and it appears to work as
expected. I can't see any reason it should disturb any existing
functionality, unless people currently use plperl to store nonlexical
variables which might now clobber each other. I think it's worth doing,
for a small but nontrivial functionality gain. If nobody objects, I will
submit a patch and some docco on how to use it.

cheers

andrew

I wrote:

Show quoted text

Thus the following perl contained in plperl.c and executed on
interpreter startup:

require Safe; SPI::bootstrap();
sub ::mksafefunc { my $x = new Safe;
$x->permit_only(':default');$x->permit(':base_math');
$x->share(qw[&elog &DEBUG &LOG &INFO &NOTICE &WARNING
&ERROR]);
return $x->reval(qq[sub { $_[0] }]); }
sub ::mkunsafefunc {return eval(qq[ sub { $_[0] } ]); }

would become something like:

require Safe; SPI::bootstrap();
use vars qw($PLContainer); $PLContainer = new
Safe("PLPerl");

$PLContainer->permit_only(':default');$PLContainer->permit(':base_math');

$PLContainer->share(qw[&elog &DEBUG &LOG &INFO &NOTICE
&WARNING &ERROR]);
sub ::mksafefunc { return $PLContainer->reval(qq[sub {
$_[0] }]); }
sub ::mkunsafefunc {return eval(qq[ sub { $_[0] } ]); }

Now you could do something like this:

create function myplperlfuncs() returns int language plperl as '
$datavar = "foo";
$funcvar = sub { return "bar"; };
return 1;
';

create function f1 () returns text language plperl as '
return $datavar;
';

create function f2() returns text language plperl as '
return &$funcvar();
';

At the start of your session you would issue "select
myplperlfuncs();" to preload the values, and thereafter you could
call f1() and f2() quite happily.