On defining Perl functions within PLPERL code - Mailing list pgsql-general

From Kynn Jones
Subject On defining Perl functions within PLPERL code
Date
Msg-id c2350ba40803101411x7d941116g8740f80ef3b0c567@mail.gmail.com
Whole thread Raw
Responses Re: On defining Perl functions within PLPERL code  ("Greg Sabino Mullane" <greg@turnstep.com>)
List pgsql-general
In a recent post I mentioned that I had a PLPERL procedure that

...adds useful definitions, mostly subs, to Perl's main package.

I thought this claim needs further clarification, since the docs for PLPERL include a warning that may give readers the impression that defining Perl functions within PLPERL code is somehow problematic.  This warning says:

Note: The use of named nested subroutines is dangerous in Perl, especially if they refer to lexical variables in the enclosing scope. Because a PL/Perl function is wrapped in a subroutine, any named subroutine you create will be nested. In general, it is far safer to create anonymous subroutines which you call via a coderef.

But this is only a problem if one defines functions like this

  sub foo {
     # blah blah blah
  }

which indeed would result in a nested function.  But one can define non-nested functions within other functions (and even "closures" that refer to lexical variables) by manipulating symbol tables directly.  For example:


CREATE FUNCTION make_closure ( TEXT ) RETURNS void
AS $PERL$
  my $lexical = shift;
  *the_closure = sub { return '>> ' . $lexical };
$PERL$ LANGUAGE plperl IMMUTABLE;

CREATE FUNCTION test_closure () RETURNS TEXT
AS $PERL$
  return the_closure();
$PERL$ LANGUAGE plperl VOLATILE;

First, we show that the perl function the_closure (or more precisely, main::the_closure) has not been defined yet. 

SELECT * from test_closure();
ERROR:  error from Perl function: Undefined subroutine &main::the_closure called at line 2.

As expected.  To define the perl function the_closure, we need to call make_closure first:

SELECT make_closure( 'foobar' );
SELECT * from test_closure();

 test_closure 
--------------
 >> foobar
(1 row)

Now the PLPERL procedure test_closure() can invoke the perl function the_closure.

The next (very contrived) example demonstrates that the functions being defined are indeed closures (note that each of the Perl functions defined through calls to make_closure2 refers to its own private copy of the variable $lexical).  (Note that this example uses a "soft reference", but one can achieve the same thing by eval'ing a suitable string).

CREATE FUNCTION make_closure2 ( TEXT, TEXT ) RETURNS void
AS $PERL$
  my $closure_name = shift;
  my $lexical = shift;
  *{ $closure_name } = sub { return '>> ' . $lexical };
  return;
$PERL$ LANGUAGE plperlu IMMUTABLE;

CREATE FUNCTION test_closure2 ( TEXT ) RETURNS TEXT
AS $PERL$
  my $ret = eval( shift() . '()' );
  die if $@;
  return $ret;
$PERL$ LANGUAGE plperlu VOLATILE;

SELECT make_closure2( 'first_closure', 'quux' );
SELECT make_closure2( 'second_closure', 'frobozz' );

SELECT * FROM test_closure2( 'first_closure' );
 test_closure2 
---------------
 >> quux
(1 row)

SELECT * FROM test_closure2( 'second_closure' );
 test_closure2 
---------------
 >> frobozz
(1 row)

And the functions need not to be defined in the main package.  For example, make_closure3 is exactly like make_closure above, except that it defines the function the_closure in the package Some::Other::Package rather than the package main.  (Similarly, test_closure3 is exactly like test_closure, except that it invokes Some::Other::Package::the_closure rather than main::the_closure.)

CREATE FUNCTION make_closure3 ( TEXT ) RETURNS void
AS $PERL$
  my $lexical = shift;
  *Some::Other::Package::the_closure = sub { return '>> ' . $lexical };
$PERL$ LANGUAGE plperl IMMUTABLE;

CREATE FUNCTION test_closure3 () RETURNS TEXT
AS $PERL$
  return Some::Other::Package::the_closure();
$PERL$ LANGUAGE plperl VOLATILE;

SELECT make_closure3( 'mwa-ha-ha-ha-ha!' );
SELECT * from test_closure3();
    test_closure3    
---------------------
 >> mwa-ha-ha-ha-ha!
(1 row)


I want to point out that such symbol-table manipulations are entirely standard.  For example, the standard Perl module Exporter uses this technique to install functions and variables in the calling package's namespace.

Kynn

pgsql-general by date:

Previous
From: Tom Lane
Date:
Subject: Re: pg_type.relacl
Next
From: John Cartwright
Date:
Subject: Re: php pg_connect fails, pgsql works