aboutsummaryrefslogtreecommitdiff
path: root/src/pl/plperl/plperl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pl/plperl/plperl.c')
-rw-r--r--src/pl/plperl/plperl.c516
1 files changed, 335 insertions, 181 deletions
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index cfad4878aa3..b4ced1ce8d4 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -49,8 +49,45 @@
/* defines PLPERL_SET_OPMASK */
#include "plperl_opmask.h"
+EXTERN_C void boot_DynaLoader(pTHX_ CV *cv);
+EXTERN_C void boot_PostgreSQL__InServer__Util(pTHX_ CV *cv);
+EXTERN_C void boot_PostgreSQL__InServer__SPI(pTHX_ CV *cv);
+
PG_MODULE_MAGIC;
+
+/**********************************************************************
+ * Information associated with a Perl interpreter. We have one interpreter
+ * that is used for all plperlu (untrusted) functions. For plperl (trusted)
+ * functions, there is a separate interpreter for each effective SQL userid.
+ * (This is needed to ensure that an unprivileged user can't inject Perl code
+ * that'll be executed with the privileges of some other SQL user.)
+ *
+ * The plperl_interp_desc structs are kept in a Postgres hash table indexed
+ * by userid OID, with OID 0 used for the single untrusted interpreter.
+ *
+ * We start out by creating a "held" interpreter, which we initialize
+ * only as far as we can do without deciding if it will be trusted or
+ * untrusted. Later, when we first need to run a plperl or plperlu
+ * function, we complete the initialization appropriately and move the
+ * PerlInterpreter pointer into the plperl_interp_hash hashtable. If after
+ * that we need more interpreters, we create them as needed if we can, or
+ * fail if the Perl build doesn't support multiple interpreters.
+ *
+ * The reason for all the dancing about with a held interpreter is to make
+ * it possible for people to preload a lot of Perl code at postmaster startup
+ * (using plperl.on_init) and then use that code in backends. Of course this
+ * will only work for the first interpreter created in any backend, but it's
+ * still useful with that restriction.
+ **********************************************************************/
+typedef struct plperl_interp_desc
+{
+ Oid user_id; /* Hash key (must be first!) */
+ PerlInterpreter *interp; /* The interpreter */
+ HTAB *query_hash; /* plperl_query_entry structs */
+} plperl_interp_desc;
+
+
/**********************************************************************
* The information we cache about loaded procedures
**********************************************************************/
@@ -59,6 +96,7 @@ typedef struct plperl_proc_desc
char *proname; /* user name of procedure */
TransactionId fn_xmin;
ItemPointerData fn_tid;
+ plperl_interp_desc *interp; /* interpreter it's created in */
bool fn_readonly;
bool lanpltrusted;
bool fn_retistuple; /* true, if function returns tuple */
@@ -73,14 +111,35 @@ typedef struct plperl_proc_desc
SV *reference;
} plperl_proc_desc;
-/* hash table entry for proc desc */
+/**********************************************************************
+ * For speedy lookup, we maintain a hash table mapping from
+ * function OID + trigger flag + user OID to plperl_proc_desc pointers.
+ * The reason the plperl_proc_desc struct isn't directly part of the hash
+ * entry is to simplify recovery from errors during compile_plperl_function.
+ *
+ * Note: if the same function is called by multiple userIDs within a session,
+ * there will be a separate plperl_proc_desc entry for each userID in the case
+ * of plperl functions, but only one entry for plperlu functions, because we
+ * set user_id = 0 for that case. If the user redeclares the same function
+ * from plperl to plperlu or vice versa, there might be multiple
+ * plperl_proc_ptr entries in the hashtable, but only one is valid.
+ **********************************************************************/
+typedef struct plperl_proc_key
+{
+ Oid proc_id; /* Function OID */
+ /*
+ * is_trigger is really a bool, but declare as Oid to ensure this struct
+ * contains no padding
+ */
+ Oid is_trigger; /* is it a trigger function? */
+ Oid user_id; /* User calling the function, or 0 */
+} plperl_proc_key;
-typedef struct plperl_proc_entry
+typedef struct plperl_proc_ptr
{
- char proc_name[NAMEDATALEN]; /* internal name, eg
- * __PLPerl_proc_39987 */
- plperl_proc_desc *proc_data;
-} plperl_proc_entry;
+ plperl_proc_key proc_key; /* Hash key (must be first!) */
+ plperl_proc_desc *proc_ptr;
+} plperl_proc_ptr;
/*
* The information we cache for the duration of a single call to a
@@ -101,7 +160,7 @@ typedef struct plperl_call_data
**********************************************************************/
typedef struct plperl_query_desc
{
- char qname[20];
+ char qname[24];
void *plan;
int nargs;
Oid *argtypes;
@@ -121,33 +180,21 @@ typedef struct plperl_query_entry
* Global data
**********************************************************************/
-typedef enum
-{
- INTERP_NONE,
- INTERP_HELD,
- INTERP_TRUSTED,
- INTERP_UNTRUSTED,
- INTERP_BOTH
-} InterpState;
-
-static InterpState interp_state = INTERP_NONE;
-
-static PerlInterpreter *plperl_trusted_interp = NULL;
-static PerlInterpreter *plperl_untrusted_interp = NULL;
-static PerlInterpreter *plperl_held_interp = NULL;
-static OP *(*pp_require_orig) (pTHX) = NULL;
-static OP *pp_require_safe(pTHX);
-static bool trusted_context;
+static HTAB *plperl_interp_hash = NULL;
static HTAB *plperl_proc_hash = NULL;
-static HTAB *plperl_query_hash = NULL;
+static plperl_interp_desc *plperl_active_interp = NULL;
+/* If we have an unassigned "held" interpreter, it's stored here */
+static PerlInterpreter *plperl_held_interp = NULL;
+/* GUC variables */
static bool plperl_use_strict = false;
static char *plperl_on_init = NULL;
static char *plperl_on_plperl_init = NULL;
static char *plperl_on_plperlu_init = NULL;
+
static bool plperl_ending = false;
+static OP *(*pp_require_orig) (pTHX) = NULL;
static char plperl_opmask[MAXO];
-static void set_interp_require(void);
/* this is saved and restored by plperl_call_handler */
static plperl_call_data *current_call_data = NULL;
@@ -163,6 +210,7 @@ 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 void set_interp_require(bool trusted);
static Datum plperl_func_handler(PG_FUNCTION_ARGS);
static Datum plperl_trigger_handler(PG_FUNCTION_ARGS);
@@ -184,7 +232,7 @@ static void plperl_exec_callback(void *arg);
static void plperl_inline_callback(void *arg);
static char *strip_trailing_ws(const char *msg);
static OP *pp_require_safe(pTHX);
-static int restore_context(bool);
+static void activate_interpreter(plperl_interp_desc *interp_desc);
#ifdef WIN32
static char *setlocale_perl(int category, char *locale);
@@ -251,8 +299,14 @@ _PG_init(void)
if (inited)
return;
+ /*
+ * Support localized messages.
+ */
pg_bindtextdomain(TEXTDOMAIN);
+ /*
+ * Initialize plperl's GUCs.
+ */
DefineCustomBoolVariable("plperl.use_strict",
gettext_noop("If true, trusted and untrusted Perl code will be compiled in strict mode."),
NULL,
@@ -261,6 +315,12 @@ _PG_init(void)
PGC_USERSET, 0,
NULL, NULL);
+ /*
+ * plperl.on_init is marked PGC_SIGHUP to support the idea that it might
+ * be executed in the postmaster (if plperl is loaded into the postmaster
+ * via shared_preload_libraries). This isn't really right either way,
+ * though.
+ */
DefineCustomStringVariable("plperl.on_init",
gettext_noop("Perl initialization code to execute when a Perl interpreter is initialized."),
NULL,
@@ -270,13 +330,18 @@ _PG_init(void)
NULL, NULL);
/*
- * plperl.on_plperl_init is currently PGC_SUSET to avoid issues whereby a
- * user who doesn't have USAGE privileges on the plperl language could
- * possibly use SET plperl.on_plperl_init='...' to influence the behaviour
- * of any existing plperl function that they can EXECUTE (which may be
- * security definer). Set
+ * plperl.on_plperl_init is marked PGC_SUSET to avoid issues whereby a
+ * user who might not even have USAGE privilege on the plperl language
+ * could nonetheless use SET plperl.on_plperl_init='...' to influence the
+ * behaviour of any existing plperl function that they can execute (which
+ * might be SECURITY DEFINER, leading to a privilege escalation). See
* http://archives.postgresql.org/pgsql-hackers/2010-02/msg00281.php and
* the overall thread.
+ *
+ * Note that because plperl.use_strict is USERSET, a nefarious user could
+ * set it to be applied against other people's functions. This is judged
+ * OK since the worst result would be an error. Your code oughta pass
+ * use_strict anyway ;-)
*/
DefineCustomStringVariable("plperl.on_plperl_init",
gettext_noop("Perl initialization code to execute once when plperl is first used."),
@@ -296,35 +361,45 @@ _PG_init(void)
EmitWarningsOnPlaceholders("plperl");
- MemSet(&hash_ctl, 0, sizeof(hash_ctl));
-
- hash_ctl.keysize = NAMEDATALEN;
- hash_ctl.entrysize = sizeof(plperl_proc_entry);
-
- plperl_proc_hash = hash_create("PLPerl Procedures",
+ /*
+ * Create hash tables.
+ */
+ memset(&hash_ctl, 0, sizeof(hash_ctl));
+ hash_ctl.keysize = sizeof(Oid);
+ hash_ctl.entrysize = sizeof(plperl_interp_desc);
+ hash_ctl.hash = oid_hash;
+ plperl_interp_hash = hash_create("PL/Perl interpreters",
+ 8,
+ &hash_ctl,
+ HASH_ELEM | HASH_FUNCTION);
+
+ memset(&hash_ctl, 0, sizeof(hash_ctl));
+ hash_ctl.keysize = sizeof(plperl_proc_key);
+ hash_ctl.entrysize = sizeof(plperl_proc_ptr);
+ hash_ctl.hash = tag_hash;
+ plperl_proc_hash = hash_create("PL/Perl procedures",
32,
&hash_ctl,
- HASH_ELEM);
-
- hash_ctl.entrysize = sizeof(plperl_query_entry);
- plperl_query_hash = hash_create("PLPerl Queries",
- 32,
- &hash_ctl,
- HASH_ELEM);
+ HASH_ELEM | HASH_FUNCTION);
+ /*
+ * Save the default opmask.
+ */
PLPERL_SET_OPMASK(plperl_opmask);
+ /*
+ * Create the first Perl interpreter, but only partially initialize it.
+ */
plperl_held_interp = plperl_init_interp();
- interp_state = INTERP_HELD;
inited = true;
}
static void
-set_interp_require(void)
+set_interp_require(bool trusted)
{
- if (trusted_context)
+ if (trusted)
{
PL_ppaddr[OP_REQUIRE] = pp_require_safe;
PL_ppaddr[OP_DOFILE] = pp_require_safe;
@@ -343,6 +418,9 @@ set_interp_require(void)
static void
plperl_fini(int code, Datum arg)
{
+ HASH_SEQ_STATUS hash_seq;
+ plperl_interp_desc *interp_desc;
+
elog(DEBUG3, "plperl_fini");
/*
@@ -360,91 +438,129 @@ plperl_fini(int code, Datum arg)
return;
}
- plperl_destroy_interp(&plperl_trusted_interp);
- plperl_destroy_interp(&plperl_untrusted_interp);
+ /* Zap the "held" interpreter, if we still have it */
plperl_destroy_interp(&plperl_held_interp);
+ /* Zap any fully-initialized interpreters */
+ hash_seq_init(&hash_seq, plperl_interp_hash);
+ while ((interp_desc = hash_seq_search(&hash_seq)) != NULL)
+ {
+ if (interp_desc->interp)
+ {
+ activate_interpreter(interp_desc);
+ plperl_destroy_interp(&interp_desc->interp);
+ }
+ }
+
elog(DEBUG3, "plperl_fini: done");
}
-/********************************************************************
- *
- * We start out by creating a "held" interpreter that we can use in
- * trusted or untrusted mode (but not both) as the need arises. Later, we
- * assign that interpreter if it is available to either the trusted or
- * untrusted interpreter. If it has already been assigned, and we need to
- * create the other interpreter, we do that if we can, or error out.
+/*
+ * Select and activate an appropriate Perl interpreter.
*/
-
-
static void
select_perl_context(bool trusted)
{
- EXTERN_C void boot_PostgreSQL__InServer__SPI(pTHX_ CV *cv);
+ Oid user_id;
+ plperl_interp_desc *interp_desc;
+ bool found;
+ PerlInterpreter *interp = NULL;
+
+ /* Find or create the interpreter hashtable entry for this userid */
+ if (trusted)
+ user_id = GetUserId();
+ else
+ user_id = InvalidOid;
+
+ interp_desc = hash_search(plperl_interp_hash, &user_id,
+ HASH_ENTER,
+ &found);
+ if (!found)
+ {
+ /* Initialize newly-created hashtable entry */
+ interp_desc->interp = NULL;
+ interp_desc->query_hash = NULL;
+ }
+
+ /* Make sure we have a query_hash for this interpreter */
+ if (interp_desc->query_hash == NULL)
+ {
+ HASHCTL hash_ctl;
+
+ memset(&hash_ctl, 0, sizeof(hash_ctl));
+ hash_ctl.keysize = NAMEDATALEN;
+ hash_ctl.entrysize = sizeof(plperl_query_entry);
+ interp_desc->query_hash = hash_create("PL/Perl queries",
+ 32,
+ &hash_ctl,
+ HASH_ELEM);
+ }
/*
- * handle simple cases
+ * Quick exit if already have an interpreter
*/
- if (restore_context(trusted))
+ if (interp_desc->interp)
+ {
+ activate_interpreter(interp_desc);
return;
+ }
/*
* adopt held interp if free, else create new one if possible
*/
- if (interp_state == INTERP_HELD)
+ if (plperl_held_interp != NULL)
{
/* first actual use of a perl interpreter */
+ interp = plperl_held_interp;
+
+ /*
+ * Reset the plperl_held_interp pointer first; if we fail during init
+ * we don't want to try again with the partially-initialized interp.
+ */
+ plperl_held_interp = NULL;
if (trusted)
- {
plperl_trusted_init();
- plperl_trusted_interp = plperl_held_interp;
- interp_state = INTERP_TRUSTED;
- }
else
- {
plperl_untrusted_init();
- plperl_untrusted_interp = plperl_held_interp;
- interp_state = INTERP_UNTRUSTED;
- }
/* successfully initialized, so arrange for cleanup */
on_proc_exit(plperl_fini, 0);
-
}
else
{
#ifdef MULTIPLICITY
- PerlInterpreter *plperl = plperl_init_interp();
+ /*
+ * plperl_init_interp will change Perl's idea of the active
+ * interpreter. Reset plperl_active_interp temporarily, so that if we
+ * hit an error partway through here, we'll make sure to switch back
+ * to a non-broken interpreter before running any other Perl
+ * functions.
+ */
+ plperl_active_interp = NULL;
+
+ /* Now build the new interpreter */
+ interp = plperl_init_interp();
if (trusted)
- {
plperl_trusted_init();
- plperl_trusted_interp = plperl;
- }
else
- {
plperl_untrusted_init();
- plperl_untrusted_interp = plperl;
- }
- interp_state = INTERP_BOTH;
#else
elog(ERROR,
- "cannot allocate second Perl interpreter on this platform");
+ "cannot allocate multiple Perl interpreters on this platform");
#endif
}
- plperl_held_interp = NULL;
- trusted_context = trusted;
- set_interp_require();
+
+ set_interp_require(trusted);
/*
* Since the timing of first use of PL/Perl can't be predicted, any
* database interaction during initialization is problematic. Including,
* but not limited to, security definer issues. So we only enable access
* to the database AFTER on_*_init code has run. See
- * http://archives.postgresql.org/message-id/20100127143318.GE713@timac.loc
- * al
+ * http://archives.postgresql.org/pgsql-hackers/2010-01/msg02669.php
*/
newXS("PostgreSQL::InServer::SPI::bootstrap",
boot_PostgreSQL__InServer__SPI, __FILE__);
@@ -454,35 +570,41 @@ select_perl_context(bool trusted)
ereport(ERROR,
(errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
errcontext("while executing PostgreSQL::InServer::SPI::bootstrap")));
+
+ /* Fully initialized, so mark the hashtable entry valid */
+ interp_desc->interp = interp;
+
+ /* And mark this as the active interpreter */
+ plperl_active_interp = interp_desc;
}
/*
- * Restore previous interpreter selection, if two are active
+ * Make the specified interpreter the active one
+ *
+ * A call with NULL does nothing. This is so that "restoring" to a previously
+ * null state of plperl_active_interp doesn't result in useless thrashing.
*/
-static int
-restore_context(bool trusted)
+static void
+activate_interpreter(plperl_interp_desc *interp_desc)
{
- if (interp_state == INTERP_BOTH ||
- (trusted && interp_state == INTERP_TRUSTED) ||
- (!trusted && interp_state == INTERP_UNTRUSTED))
+ if (interp_desc && plperl_active_interp != interp_desc)
{
- if (trusted_context != trusted)
- {
- if (trusted)
- PERL_SET_CONTEXT(plperl_trusted_interp);
- else
- PERL_SET_CONTEXT(plperl_untrusted_interp);
-
- trusted_context = trusted;
- set_interp_require();
- }
- return 1; /* context restored */
+ Assert(interp_desc->interp);
+ PERL_SET_CONTEXT(interp_desc->interp);
+ /* trusted iff user_id isn't InvalidOid */
+ set_interp_require(OidIsValid(interp_desc->user_id));
+ plperl_active_interp = interp_desc;
}
-
- return 0; /* unable - appropriate interpreter not
- * available */
}
+/*
+ * Create a new Perl interpreter.
+ *
+ * We initialize the interpreter as far as we can without knowing whether
+ * it will become a trusted or untrusted interpreter; in particular, the
+ * plperl.on_init code will get executed. Later, either plperl_trusted_init
+ * or plperl_untrusted_init must be called to complete the initialization.
+ */
static PerlInterpreter *
plperl_init_interp(void)
{
@@ -538,17 +660,17 @@ plperl_init_interp(void)
STMT_START { \
if (saved != NULL) { setlocale_perl(name, saved); pfree(saved); } \
} STMT_END
-#endif
+#endif /* WIN32 */
- if (plperl_on_init)
+ if (plperl_on_init && *plperl_on_init)
{
embedding[nargs++] = "-e";
embedding[nargs++] = plperl_on_init;
}
- /****
+ /*
* The perl API docs state that PERL_SYS_INIT3 should be called before
- * allocating interprters. Unfortunately, on some platforms this fails
+ * allocating interpreters. Unfortunately, on some platforms this fails
* in the Perl_do_taint() routine, which is called when the platform is
* using the system's malloc() instead of perl's own. Other platforms,
* notably Windows, fail if PERL_SYS_INIT3 is not called. So we call it
@@ -655,6 +777,11 @@ pp_require_safe(pTHX)
}
+/*
+ * Destroy one Perl interpreter ... actually we just run END blocks.
+ *
+ * Caller must have ensured this interpreter is the active one.
+ */
static void
plperl_destroy_interp(PerlInterpreter **interp)
{
@@ -671,8 +798,6 @@ plperl_destroy_interp(PerlInterpreter **interp)
* 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)
{
@@ -692,7 +817,9 @@ plperl_destroy_interp(PerlInterpreter **interp)
}
}
-
+/*
+ * Initialize the current Perl interpreter as a trusted interp
+ */
static void
plperl_trusted_init(void)
{
@@ -770,9 +897,15 @@ plperl_trusted_init(void)
}
+/*
+ * Initialize the current Perl interpreter as an untrusted interp
+ */
static void
plperl_untrusted_init(void)
{
+ /*
+ * Nothing to do except execute plperl.on_plperlu_init
+ */
if (plperl_on_plperlu_init && *plperl_on_plperlu_init)
{
eval_pv(plperl_on_plperlu_init, FALSE);
@@ -1077,7 +1210,7 @@ plperl_call_handler(PG_FUNCTION_ARGS)
{
Datum retval;
plperl_call_data *save_call_data = current_call_data;
- bool oldcontext = trusted_context;
+ plperl_interp_desc *oldinterp = plperl_active_interp;
PG_TRY();
{
@@ -1089,13 +1222,13 @@ plperl_call_handler(PG_FUNCTION_ARGS)
PG_CATCH();
{
current_call_data = save_call_data;
- restore_context(oldcontext);
+ activate_interpreter(oldinterp);
PG_RE_THROW();
}
PG_END_TRY();
current_call_data = save_call_data;
- restore_context(oldcontext);
+ activate_interpreter(oldinterp);
return retval;
}
@@ -1112,7 +1245,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
FmgrInfo flinfo;
plperl_proc_desc desc;
plperl_call_data *save_call_data = current_call_data;
- bool oldcontext = trusted_context;
+ plperl_interp_desc *oldinterp = plperl_active_interp;
ErrorContextCallback pl_error_context;
/* Set up a callback for error reporting */
@@ -1175,7 +1308,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
if (desc.reference)
SvREFCNT_dec(desc.reference);
current_call_data = save_call_data;
- restore_context(oldcontext);
+ activate_interpreter(oldinterp);
PG_RE_THROW();
}
PG_END_TRY();
@@ -1184,7 +1317,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
SvREFCNT_dec(desc.reference);
current_call_data = save_call_data;
- restore_context(oldcontext);
+ activate_interpreter(oldinterp);
error_context_stack = pl_error_context.previous;
@@ -1336,8 +1469,6 @@ 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::Util::bootstrap",
@@ -1535,7 +1666,7 @@ plperl_func_handler(PG_FUNCTION_ARGS)
"cannot accept a set")));
}
- select_perl_context(prodesc->lanpltrusted);
+ activate_interpreter(prodesc->interp);
perlret = plperl_call_perl_func(prodesc, fcinfo);
@@ -1682,7 +1813,7 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
pl_error_context.arg = prodesc->proname;
error_context_stack = &pl_error_context;
- select_perl_context(prodesc->lanpltrusted);
+ activate_interpreter(prodesc->interp);
svTD = plperl_trigger_build_args(fcinfo);
perlret = plperl_call_perl_trigger_func(prodesc, fcinfo, svTD);
@@ -1762,17 +1893,54 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
}
+static bool
+validate_plperl_function(plperl_proc_ptr *proc_ptr, HeapTuple procTup)
+{
+ if (proc_ptr && proc_ptr->proc_ptr)
+ {
+ plperl_proc_desc *prodesc = proc_ptr->proc_ptr;
+ bool uptodate;
+
+ /************************************************************
+ * If it's present, must check whether it's still up to date.
+ * This is needed because CREATE OR REPLACE FUNCTION can modify the
+ * function's pg_proc entry without changing its OID.
+ ************************************************************/
+ uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
+ ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
+
+ if (uptodate)
+ return true;
+
+ /* Otherwise, unlink the obsoleted entry from the hashtable ... */
+ proc_ptr->proc_ptr = NULL;
+ /* ... and throw it away */
+ if (prodesc->reference)
+ {
+ plperl_interp_desc *oldinterp = plperl_active_interp;
+
+ activate_interpreter(prodesc->interp);
+ SvREFCNT_dec(prodesc->reference);
+ activate_interpreter(oldinterp);
+ }
+ free(prodesc->proname);
+ free(prodesc);
+ }
+
+ return false;
+}
+
+
static plperl_proc_desc *
compile_plperl_function(Oid fn_oid, bool is_trigger)
{
HeapTuple procTup;
Form_pg_proc procStruct;
- char internal_proname[NAMEDATALEN];
+ plperl_proc_key proc_key;
+ plperl_proc_ptr *proc_ptr;
plperl_proc_desc *prodesc = NULL;
int i;
- plperl_proc_entry *hash_entry;
- bool found;
- bool oldcontext = trusted_context;
+ plperl_interp_desc *oldinterp = plperl_active_interp;
ErrorContextCallback plperl_error_context;
/* We'll need the pg_proc tuple in any case... */
@@ -1787,48 +1955,24 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
plperl_error_context.arg = NameStr(procStruct->proname);
error_context_stack = &plperl_error_context;
- /************************************************************
- * Build our internal proc name from the function's Oid
- ************************************************************/
- if (!is_trigger)
- sprintf(internal_proname, "__PLPerl_proc_%u", fn_oid);
- else
- sprintf(internal_proname, "__PLPerl_proc_%u_trigger", fn_oid);
+ /* Try to find function in plperl_proc_hash */
+ proc_key.proc_id = fn_oid;
+ proc_key.is_trigger = is_trigger;
+ proc_key.user_id = GetUserId();
- /************************************************************
- * Lookup the internal proc name in the hashtable
- ************************************************************/
- hash_entry = hash_search(plperl_proc_hash, internal_proname,
- HASH_FIND, NULL);
+ proc_ptr = hash_search(plperl_proc_hash, &proc_key,
+ HASH_FIND, NULL);
- if (hash_entry)
+ if (validate_plperl_function(proc_ptr, procTup))
+ prodesc = proc_ptr->proc_ptr;
+ else
{
- bool uptodate;
-
- prodesc = hash_entry->proc_data;
-
- /************************************************************
- * If it's present, must check whether it's still up to date.
- * This is needed because CREATE OR REPLACE FUNCTION can modify the
- * function's pg_proc entry without changing its OID.
- ************************************************************/
- uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
- ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
-
- if (!uptodate)
- {
- hash_search(plperl_proc_hash, internal_proname,
- HASH_REMOVE, NULL);
- if (prodesc->reference)
- {
- select_perl_context(prodesc->lanpltrusted);
- SvREFCNT_dec(prodesc->reference);
- restore_context(oldcontext);
- }
- free(prodesc->proname);
- free(prodesc);
- prodesc = NULL;
- }
+ /* If not found or obsolete, maybe it's plperlu */
+ proc_key.user_id = InvalidOid;
+ proc_ptr = hash_search(plperl_proc_hash, &proc_key,
+ HASH_FIND, NULL);
+ if (validate_plperl_function(proc_ptr, procTup))
+ prodesc = proc_ptr->proc_ptr;
}
/************************************************************
@@ -1859,6 +2003,10 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
errmsg("out of memory")));
MemSet(prodesc, 0, sizeof(plperl_proc_desc));
prodesc->proname = strdup(NameStr(procStruct->proname));
+ if (prodesc->proname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of memory")));
prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
prodesc->fn_tid = procTup->t_self;
@@ -1996,27 +2144,33 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
proc_source = TextDatumGetCString(prosrcdatum);
/************************************************************
- * Create the procedure in the interpreter
+ * Create the procedure in the appropriate interpreter
************************************************************/
select_perl_context(prodesc->lanpltrusted);
+ prodesc->interp = plperl_active_interp;
+
plperl_create_sub(prodesc, proc_source, fn_oid);
- restore_context(oldcontext);
+ activate_interpreter(oldinterp);
pfree(proc_source);
if (!prodesc->reference) /* can this happen? */
{
free(prodesc->proname);
free(prodesc);
- elog(ERROR, "could not create internal procedure \"%s\"",
- internal_proname);
+ elog(ERROR, "could not create PL/Perl internal procedure");
}
- hash_entry = hash_search(plperl_proc_hash, internal_proname,
- HASH_ENTER, &found);
- hash_entry->proc_data = prodesc;
+ /************************************************************
+ * OK, link the procedure into the correct hashtable entry
+ ************************************************************/
+ proc_key.user_id = prodesc->lanpltrusted ? GetUserId() : InvalidOid;
+
+ proc_ptr = hash_search(plperl_proc_hash, &proc_key,
+ HASH_ENTER, NULL);
+ proc_ptr->proc_ptr = prodesc;
}
/* restore previous error callback */
@@ -2636,7 +2790,7 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
* the key to the caller.
************************************************************/
- hash_entry = hash_search(plperl_query_hash, qdesc->qname,
+ hash_entry = hash_search(plperl_active_interp->query_hash, qdesc->qname,
HASH_ENTER, &found);
hash_entry->query_data = qdesc;
@@ -2675,7 +2829,7 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
* Fetch the saved plan descriptor, see if it's o.k.
************************************************************/
- hash_entry = hash_search(plperl_query_hash, query,
+ hash_entry = hash_search(plperl_active_interp->query_hash, query,
HASH_FIND, NULL);
if (hash_entry == NULL)
elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
@@ -2683,7 +2837,7 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
qdesc = hash_entry->query_data;
if (qdesc == NULL)
- elog(ERROR, "spi_exec_prepared: panic - plperl_query_hash value vanished");
+ elog(ERROR, "spi_exec_prepared: panic - plperl query_hash value vanished");
if (qdesc->nargs != argc)
elog(ERROR, "spi_exec_prepared: expected %d argument(s), %d passed",
@@ -2818,7 +2972,7 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
/************************************************************
* Fetch the saved plan descriptor, see if it's o.k.
************************************************************/
- hash_entry = hash_search(plperl_query_hash, query,
+ hash_entry = hash_search(plperl_active_interp->query_hash, query,
HASH_FIND, NULL);
if (hash_entry == NULL)
elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
@@ -2826,7 +2980,7 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
qdesc = hash_entry->query_data;
if (qdesc == NULL)
- elog(ERROR, "spi_query_prepared: panic - plperl_query_hash value vanished");
+ elog(ERROR, "spi_query_prepared: panic - plperl query_hash value vanished");
if (qdesc->nargs != argc)
elog(ERROR, "spi_query_prepared: expected %d argument(s), %d passed",
@@ -2934,7 +3088,7 @@ plperl_spi_freeplan(char *query)
check_spi_usage_allowed();
- hash_entry = hash_search(plperl_query_hash, query,
+ hash_entry = hash_search(plperl_active_interp->query_hash, query,
HASH_FIND, NULL);
if (hash_entry == NULL)
elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
@@ -2942,13 +3096,13 @@ plperl_spi_freeplan(char *query)
qdesc = hash_entry->query_data;
if (qdesc == NULL)
- elog(ERROR, "spi_exec_freeplan: panic - plperl_query_hash value vanished");
+ elog(ERROR, "spi_exec_freeplan: panic - plperl query_hash value vanished");
/*
* free all memory before SPI_freeplan, so if it dies, nothing will be
* left over
*/
- hash_search(plperl_query_hash, query,
+ hash_search(plperl_active_interp->query_hash, query,
HASH_REMOVE, NULL);
plan = qdesc->plan;