Commit 85d67ccd authored by Andrew Dunstan's avatar Andrew Dunstan

Add plperl.on_perl_init setting to provide for initializing the perl library...

Add plperl.on_perl_init setting to provide for initializing the perl library on load. Also, handle END blocks in plperl.
Database access is disallowed during both these operations, although it might be allowed in END blocks in future.

Patch from Tim Bunce.
parent 29eedd31
<!-- $PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.76 2010/01/27 02:55:04 adunstan Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.77 2010/01/30 01:46:57 adunstan Exp $ -->
<chapter id="plperl">
<title>PL/Perl - Perl Procedural Language</title>
......@@ -1028,7 +1028,72 @@ CREATE TRIGGER test_valid_id_trig
</para>
</sect1>
<sect1 id="plperl-missing">
<sect1 id="plperl-under-the-hood">
<title>PL/Perl Under the Hood</title>
<sect2 id="plperl-config">
<title>Configuration</title>
<para>
This section lists configuration parameters that affect <application>PL/Perl</>.
To set any of these parameters 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>
<variablelist>
<varlistentry id="guc-plperl-on-perl-init" xreflabel="plperl.on_perl_init">
<term><varname>plperl.on_perl_init</varname> (<type>string</type>)</term>
<indexterm>
<primary><varname>plperl.on_perl_init</> configuration parameter</primary>
</indexterm>
<listitem>
<para>
Specifies perl code to be executed when a perl interpreter is first initialized.
The SPI functions are not available when this code is executed.
If the code fails with an error it will abort the initialization of the interpreter
and propagate out to the calling query, causing the current transaction
or subtransaction to be aborted.
</para>
<para>
The perl code is limited to a single string. Longer code can be placed
into a module and loaded by the <literal>on_perl_init</> string.
Examples:
<programlisting>
plplerl.on_perl_init = '$ENV{NYTPROF}="start=no"; require Devel::NYTProf::PgPLPerl'
plplerl.on_perl_init = 'use lib "/my/app"; use MyApp::PgInit;'
</programlisting>
</para>
<para>
Initialization will happen in the postmaster if the plperl library is included
in <literal>shared_preload_libraries</> (see <xref linkend="guc-shared-preload-libraries">),
in which case extra consideration should be given to the risk of destabilizing the postmaster.
</para>
<para>
This parameter can only be set in the postgresql.conf file or on the server command line.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-plperl-use-strict" xreflabel="plperl.use_strict">
<term><varname>plperl.use_strict</varname> (<type>boolean</type>)</term>
<indexterm>
<primary><varname>plperl.use_strict</> configuration parameter</primary>
</indexterm>
<listitem>
<para>
When set true subsequent compilations of PL/Perl functions have the <literal>strict</> pragma enabled.
This parameter does not affect functions already compiled in the current session.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
<sect2 id="plperl-missing">
<title>Limitations and Missing Features</title>
<para>
......@@ -1063,10 +1128,21 @@ CREATE TRIGGER test_valid_id_trig
<literal>return_next</literal> for each row returned, as shown
previously.
</para>
</listitem>
<listitem>
<para>
When a session ends normally, not due to a fatal error, any
<literal>END</> blocks that have been defined are executed.
Currently no other actions are performed. Specifically,
file handles are not automatically flushed and objects are
not automatically destroyed.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
</sect1>
</chapter>
# $PostgreSQL: pgsql/src/pl/plperl/plc_perlboot.pl,v 1.3 2010/01/26 23:11:56 adunstan Exp $
# $PostgreSQL: pgsql/src/pl/plperl/plc_perlboot.pl,v 1.4 2010/01/30 01:46:57 adunstan Exp $
PostgreSQL::InServer::Util::bootstrap();
PostgreSQL::InServer::SPI::bootstrap();
use strict;
use warnings;
......
/**********************************************************************
* plperl.c - perl as a procedural language for PostgreSQL
*
* $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.162 2010/01/28 23:06:09 adunstan Exp $
* $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.163 2010/01/30 01:46:57 adunstan Exp $
*
**********************************************************************/
......@@ -27,6 +27,7 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
#include "storage/ipc.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
......@@ -138,6 +139,8 @@ static HTAB *plperl_proc_hash = NULL;
static HTAB *plperl_query_hash = NULL;
static bool plperl_use_strict = false;
static char *plperl_on_perl_init = NULL;
static bool plperl_ending = false;
/* this is saved and restored by plperl_call_handler */
static plperl_call_data *current_call_data = NULL;
......@@ -151,6 +154,8 @@ Datum plperl_validator(PG_FUNCTION_ARGS);
void _PG_init(void);
static PerlInterpreter *plperl_init_interp(void);
static void plperl_destroy_interp(PerlInterpreter **);
static void plperl_fini(int code, Datum arg);
static Datum plperl_func_handler(PG_FUNCTION_ARGS);
static Datum plperl_trigger_handler(PG_FUNCTION_ARGS);
......@@ -237,6 +242,14 @@ _PG_init(void)
PGC_USERSET, 0,
NULL, NULL);
DefineCustomStringVariable("plperl.on_perl_init",
gettext_noop("Perl code to execute when the perl interpreter is initialized."),
NULL,
&plperl_on_perl_init,
NULL,
PGC_SIGHUP, 0,
NULL, NULL);
EmitWarningsOnPlaceholders("plperl");
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
......@@ -261,6 +274,37 @@ _PG_init(void)
inited = true;
}
/*
* Cleanup perl interpreters, including running END blocks.
* Does not fully undo the actions of _PG_init() nor make it callable again.
*/
static void
plperl_fini(int code, Datum arg)
{
elog(DEBUG3, "plperl_fini");
/*
* Disable use of spi_* functions when running END/DESTROY code.
* Could be enabled in future, with care, using a transaction
* http://archives.postgresql.org/pgsql-hackers/2010-01/msg02743.php
*/
plperl_ending = true;
/* Only perform perl cleanup if we're exiting cleanly */
if (code) {
elog(DEBUG3, "plperl_fini: skipped");
return;
}
plperl_destroy_interp(&plperl_trusted_interp);
plperl_destroy_interp(&plperl_untrusted_interp);
plperl_destroy_interp(&plperl_held_interp);
elog(DEBUG3, "plperl_fini: done");
}
#define SAFE_MODULE \
"require Safe; $Safe::VERSION"
......@@ -277,6 +321,8 @@ _PG_init(void)
static void
select_perl_context(bool trusted)
{
EXTERN_C void boot_PostgreSQL__InServer__SPI(pTHX_ CV *cv);
/*
* handle simple cases
*/
......@@ -288,6 +334,10 @@ select_perl_context(bool trusted)
*/
if (interp_state == INTERP_HELD)
{
/* first actual use of a perl interpreter */
on_proc_exit(plperl_fini, 0);
if (trusted)
{
plperl_trusted_interp = plperl_held_interp;
......@@ -325,6 +375,22 @@ select_perl_context(bool trusted)
plperl_safe_init();
PL_ppaddr[OP_REQUIRE] = pp_require_safe;
}
/*
* enable access to the database
*/
newXS("PostgreSQL::InServer::SPI::bootstrap",
boot_PostgreSQL__InServer__SPI, __FILE__);
eval_pv("PostgreSQL::InServer::SPI::bootstrap()", FALSE);
if (SvTRUE(ERRSV))
{
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
errdetail("While executing PostgreSQL::InServer::SPI::bootstrap")));
}
}
/*
......@@ -361,7 +427,7 @@ plperl_init_interp(void)
PerlInterpreter *plperl;
static int perl_sys_init_done;
static char *embedding[3] = {
static char *embedding[3+2] = {
"", "-e", PLC_PERLBOOT
};
int nargs = 3;
......@@ -408,6 +474,12 @@ plperl_init_interp(void)
save_time = loc ? pstrdup(loc) : NULL;
#endif
if (plperl_on_perl_init)
{
embedding[nargs++] = "-e";
embedding[nargs++] = plperl_on_perl_init;
}
/****
* The perl API docs state that PERL_SYS_INIT3 should be called before
* allocating interprters. Unfortunately, on some platforms this fails
......@@ -437,6 +509,9 @@ plperl_init_interp(void)
PERL_SET_CONTEXT(plperl);
perl_construct(plperl);
/* run END blocks in perl_destruct instead of perl_run */
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
/*
* Record the original function for the 'require' opcode.
* Ensure it's used for new interpreters.
......@@ -446,9 +521,18 @@ plperl_init_interp(void)
else
PL_ppaddr[OP_REQUIRE] = pp_require_orig;
perl_parse(plperl, plperl_init_shared_libs,
nargs, embedding, NULL);
perl_run(plperl);
if (perl_parse(plperl, plperl_init_shared_libs,
nargs, embedding, NULL) != 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("while parsing perl initialization"),
errdetail("%s", strip_trailing_ws(SvPV_nolen(ERRSV))) ));
if (perl_run(plperl) != 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("while running perl initialization"),
errdetail("%s", strip_trailing_ws(SvPV_nolen(ERRSV))) ));
#ifdef WIN32
......@@ -523,6 +607,43 @@ pp_require_safe(pTHX)
}
static void
plperl_destroy_interp(PerlInterpreter **interp)
{
if (interp && *interp)
{
/*
* Only a very minimal destruction is performed:
* - just call END blocks.
*
* We could call perl_destruct() but we'd need to audit its
* actions very carefully and work-around any that impact us.
* (Calling sv_clean_objs() isn't an option because it's not
* part of perl's public API so isn't portably available.)
* Meanwhile END blocks can be used to perform manual cleanup.
*/
PERL_SET_CONTEXT(*interp);
/* Run END blocks - based on perl's perl_destruct() */
if (PL_exit_flags & PERL_EXIT_DESTRUCT_END) {
dJMPENV;
int x = 0;
JMPENV_PUSH(x);
PERL_UNUSED_VAR(x);
if (PL_endav && !PL_minus_c)
call_list(PL_scopestack_ix, PL_endav);
JMPENV_POP;
}
LEAVE;
FREETMPS;
*interp = NULL;
}
}
static void
plperl_safe_init(void)
{
......@@ -544,8 +665,8 @@ plperl_safe_init(void)
{
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
errdetail("While executing PLC_SAFE_BAD")));
errmsg("while executing PLC_SAFE_BAD"),
errdetail("%s", strip_trailing_ws(SvPV_nolen(ERRSV))) ));
}
}
......@@ -556,8 +677,8 @@ plperl_safe_init(void)
{
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
errdetail("While executing PLC_SAFE_OK")));
errmsg("while executing PLC_SAFE_OK"),
errdetail("%s", strip_trailing_ws(SvPV_nolen(ERRSV))) ));
}
if (GetDatabaseEncoding() == PG_UTF8)
......@@ -1153,18 +1274,14 @@ plperl_create_sub(plperl_proc_desc *prodesc, char *s, Oid fn_oid)
*
**********************************************************************/
EXTERN_C void boot_DynaLoader(pTHX_ CV *cv);
EXTERN_C void boot_PostgreSQL__InServer__SPI(pTHX_ CV *cv);
EXTERN_C void boot_PostgreSQL__InServer__Util(pTHX_ CV *cv);
static void
plperl_init_shared_libs(pTHX)
{
char *file = __FILE__;
EXTERN_C void boot_DynaLoader(pTHX_ CV *cv);
EXTERN_C void boot_PostgreSQL__InServer__Util(pTHX_ CV *cv);
newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
newXS("PostgreSQL::InServer::SPI::bootstrap",
boot_PostgreSQL__InServer__SPI, file);
newXS("PostgreSQL::InServer::Util::bootstrap",
boot_PostgreSQL__InServer__Util, file);
}
......@@ -1900,6 +2017,16 @@ plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc)
}
static void
check_spi_usage_allowed()
{
if (plperl_ending) {
/* simple croak as we don't want to involve PostgreSQL code */
croak("SPI functions can not be used in END blocks");
}
}
HV *
plperl_spi_exec(char *query, int limit)
{
......@@ -1912,6 +2039,8 @@ plperl_spi_exec(char *query, int limit)
MemoryContext oldcontext = CurrentMemoryContext;
ResourceOwner oldowner = CurrentResourceOwner;
check_spi_usage_allowed();
BeginInternalSubTransaction(NULL);
/* Want to run inside function's memory context */
MemoryContextSwitchTo(oldcontext);
......@@ -1975,6 +2104,8 @@ plperl_spi_execute_fetch_result(SPITupleTable *tuptable, int processed,
{
HV *result;
check_spi_usage_allowed();
result = newHV();
hv_store_string(result, "status",
......@@ -2148,6 +2279,8 @@ plperl_spi_query(char *query)
MemoryContext oldcontext = CurrentMemoryContext;
ResourceOwner oldowner = CurrentResourceOwner;
check_spi_usage_allowed();
BeginInternalSubTransaction(NULL);
/* Want to run inside function's memory context */
MemoryContextSwitchTo(oldcontext);
......@@ -2226,6 +2359,8 @@ plperl_spi_fetchrow(char *cursor)
MemoryContext oldcontext = CurrentMemoryContext;
ResourceOwner oldowner = CurrentResourceOwner;
check_spi_usage_allowed();
BeginInternalSubTransaction(NULL);
/* Want to run inside function's memory context */
MemoryContextSwitchTo(oldcontext);
......@@ -2300,7 +2435,11 @@ plperl_spi_fetchrow(char *cursor)
void
plperl_spi_cursor_close(char *cursor)
{
Portal p = SPI_cursor_find(cursor);
Portal p;
check_spi_usage_allowed();
p = SPI_cursor_find(cursor);
if (p)
SPI_cursor_close(p);
......@@ -2318,6 +2457,8 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
MemoryContext oldcontext = CurrentMemoryContext;
ResourceOwner oldowner = CurrentResourceOwner;
check_spi_usage_allowed();
BeginInternalSubTransaction(NULL);
MemoryContextSwitchTo(oldcontext);
......@@ -2453,6 +2594,8 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
MemoryContext oldcontext = CurrentMemoryContext;
ResourceOwner oldowner = CurrentResourceOwner;
check_spi_usage_allowed();
BeginInternalSubTransaction(NULL);
/* Want to run inside function's memory context */
MemoryContextSwitchTo(oldcontext);
......@@ -2595,6 +2738,8 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
MemoryContext oldcontext = CurrentMemoryContext;
ResourceOwner oldowner = CurrentResourceOwner;
check_spi_usage_allowed();
BeginInternalSubTransaction(NULL);
/* Want to run inside function's memory context */
MemoryContextSwitchTo(oldcontext);
......@@ -2718,6 +2863,8 @@ plperl_spi_freeplan(char *query)
plperl_query_desc *qdesc;
plperl_query_entry *hash_entry;
check_spi_usage_allowed();
hash_entry = hash_search(plperl_query_hash, query,
HASH_FIND, NULL);
if (hash_entry == NULL)
......
-- test END block handling
-- Not included in the normal testing
-- because it's beyond the scope of the test harness.
-- Available here for manual developer testing.
DO $do$
my $testlog = "/tmp/pgplperl_test.log";
warn "Run test, then examine contents of $testlog (which must already exist)\n";
return unless -f $testlog;
use IO::Handle; # for autoflush
open my $fh, '>', $testlog
or die "Can't write to $testlog: $!";
$fh->autoflush(1);
print $fh "# you should see just 3 'Warn: ...' lines: PRE, END and SPI ...\n";
$SIG{__WARN__} = sub { print $fh "Warn: @_" };
$SIG{__DIE__} = sub { print $fh "Die: @_" unless $^S; die @_ };
END {
warn "END\n";
eval { spi_exec_query("select 1") };
warn $@;
}
warn "PRE\n";
$do$ language plperlu;
......@@ -16,4 +16,3 @@ $$ 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)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment