mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-03-07 19:47:50 +08:00
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.
This commit is contained in:
parent
29eedd3122
commit
85d67ccd75
@ -1,4 +1,4 @@
|
||||
<!-- $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>
|
||||
|
@ -1,8 +1,7 @@
|
||||
|
||||
# $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;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**********************************************************************
|
||||
* 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)
|
||||
|
29
src/pl/plperl/sql/plperl_end.sql
Normal file
29
src/pl/plperl/sql/plperl_end.sql
Normal file
@ -0,0 +1,29 @@
|
||||
-- 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)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user