WIP 2 interpreters for plperl

Started by Andrew Dunstanabout 19 years ago10 messages
#1Andrew Dunstan
andrew@dunslane.net
1 attachment(s)

I have made some progress with what I think is needed to have two
interpreters for plperl. This is a lot harder than the pltcl case for
two reasons: 1. there are no restrictions on having 2 tcl interpreters,
and 2. tcl does not need to save and restore context as we have to do
with perl. I think I have a conceptual siolution to these two problems,
but what I have is currently segfaulting somewhat myteriously. Tracing a
dynamically loaded library in a postgres backend with a debugger is less
than fun, too. I am attaching what I currently have, liberally sprinkled
with elog(NOTICE) calls as trace writes.

I need to get some other work done today too, so I will return to this
later if I can. In the meanwhile, if anybody cares to cast a fresh set
of eyeballs over this, please do.

cheers

andrew

Attachments:

perldifftext/plain; name=perldiffDownload
Index: plperl.c
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plperl/plperl.c,v
retrieving revision 1.121
diff -c -r1.121 plperl.c
*** plperl.c	19 Oct 2006 18:32:47 -0000	1.121
--- plperl.c	5 Nov 2006 20:27:32 -0000
***************
*** 27,32 ****
--- 27,33 ----
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  #include "utils/typcache.h"
+ #include "utils/hsearch.h"
  
  /* perl stuff */
  #include "plperl.h"
***************
*** 55,60 ****
--- 56,69 ----
  	SV		   *reference;
  } plperl_proc_desc;
  
+ /* hash table entry for proc desc  */
+ 
+ typedef struct plperl_proc_entry
+ {
+ 	char proc_name[NAMEDATALEN];
+ 	plperl_proc_desc *proc_data;
+ } plperl_proc_entry;
+ 
  /*
   * The information we cache for the duration of a single call to a
   * function.
***************
*** 82,94 ****
  	Oid		   *argtypioparams;
  } plperl_query_desc;
  
  /**********************************************************************
   * Global data
   **********************************************************************/
  static bool plperl_safe_init_done = false;
! static PerlInterpreter *plperl_interp = NULL;
! static HV  *plperl_proc_hash = NULL;
! static HV  *plperl_query_hash = NULL;
  
  static bool plperl_use_strict = false;
  
--- 91,128 ----
  	Oid		   *argtypioparams;
  } plperl_query_desc;
  
+ /* hash table entry for query desc  */
+ 
+ typedef struct plperl_query_entry
+ {
+ 	char query_name[NAMEDATALEN];
+ 	plperl_query_desc *query_data;
+ } 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 bool can_run_two = false;
+ 
  static bool plperl_safe_init_done = false;
! static PerlInterpreter *plperl_trusted_interp = NULL;
! static PerlInterpreter *plperl_untrusted_interp = NULL;
! static PerlInterpreter *plperl_held_interp = NULL;
! static bool can_run_two;
! static bool trusted_context;
! static HTAB  *plperl_proc_hash = NULL;
! static HTAB  *plperl_query_hash = NULL;
  
  static bool plperl_use_strict = false;
  
***************
*** 144,149 ****
--- 178,184 ----
  {
  	/* Be sure we do initialization only once (should be redundant now) */
  	static bool inited = false;
+     HASHCTL     hash_ctl;
  
  	if (inited)
  		return;
***************
*** 157,162 ****
--- 192,213 ----
  
  	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",
+ 								   32,
+ 								   &hash_ctl,
+ 								   HASH_ELEM);
+ 
+ 	hash_ctl.entrysize = sizeof(plperl_query_entry);
+ 	plperl_query_hash = hash_create("PLPerl Queries",
+ 									32,
+ 									&hash_ctl,
+ 									HASH_ELEM);
+ 
  	plperl_init_interp();
  
  	inited = true;
***************
*** 235,240 ****
--- 286,381 ----
  	"      elog(ERROR,'trusted Perl functions disabled - " \
  	"      please upgrade Perl Safe module to version 2.09 or later');}]); }"
  
+ #define TEST_FOR_MULTI \
+ 	"use Config; " \
+ 	"$Config{usemultiplicity} eq 'define' or "  \
+     "($Config{usethreads} eq 'define' " \
+ 	" and $Config{useithreads} eq 'define')"
+ 
+ 
+ /********************************************************************
+  *
+  * 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.
+  * We detect if it is safe to run two interpreters during the setup of the
+  * dummy interpreter.
+  */
+ 
+ 
+ static void 
+ check_interp(bool trusted)
+ {
+ 	elog(NOTICE,"starting check_interp");
+ 	if (interp_state == INTERP_HELD)
+ 	{
+ 		if (trusted)
+ 		{
+ 			plperl_trusted_interp = plperl_held_interp;
+ 			interp_state = INTERP_TRUSTED;
+ 		}
+ 		else
+ 		{
+ 			plperl_untrusted_interp = plperl_held_interp;
+ 			interp_state = INTERP_UNTRUSTED;
+ 		}
+ 		plperl_held_interp = NULL;
+ 		trusted_context = trusted;
+ 	}
+ 	else if (interp_state == INTERP_BOTH || 
+ 			 (trusted && interp_state == INTERP_TRUSTED) ||
+ 			 (!trusted && interp_state == INTERP_UNTRUSTED))
+ 	{
+ 		if (trusted_context != trusted)
+ 		{
+ 			if (trusted)
+ 				PERL_SET_CONTEXT(plperl_trusted_interp);
+ 			else
+ 				PERL_SET_CONTEXT(plperl_untrusted_interp);
+ 			trusted_context = trusted;
+ 		}
+ 	}
+ 	else if (can_run_two)
+ 	{
+ 		PERL_SET_CONTEXT(plperl_held_interp);
+ 		plperl_init_interp();
+ 		if (trusted)
+ 			plperl_trusted_interp = plperl_held_interp;
+ 		else
+ 			plperl_untrusted_interp = plperl_held_interp;
+ 		interp_state = INTERP_BOTH;
+ 		plperl_held_interp = NULL;
+ 		trusted_context = trusted;
+ 	}
+ 	else
+ 	{
+ 		elog(ERROR, 
+ 			 "can not allocate second Perl interpreter on this platform");
+ 
+ 	}
+ 	elog(NOTICE,"leaving check_interp");
+ 	
+ }
+ 
+ 
+ static void
+ restore_context (bool old_context)
+ {
+ 	elog(NOTICE,"starting restore_context");
+ 
+ 	if (trusted_context != old_context)
+ 	{
+ 		if (old_context)
+ 			PERL_SET_CONTEXT(plperl_trusted_interp);
+ 		else
+ 			PERL_SET_CONTEXT(plperl_untrusted_interp);
+ 		trusted_context = old_context;
+ 	}
+ 	elog(NOTICE,"leaving restore_context");
+ 
+ }
  
  static void
  plperl_init_interp(void)
***************
*** 285,301 ****
  	save_time = loc ? pstrdup(loc) : NULL;
  #endif
  
! 	plperl_interp = perl_alloc();
! 	if (!plperl_interp)
  		elog(ERROR, "could not allocate Perl interpreter");
  
! 	perl_construct(plperl_interp);
! 	perl_parse(plperl_interp, plperl_init_shared_libs, 3, embedding, NULL);
! 	perl_run(plperl_interp);
  
! 	plperl_proc_hash = newHV();
! 	plperl_query_hash = newHV();
  
  #ifdef WIN32
  
  	eval_pv("use POSIX qw(locale_h);", TRUE);	/* croak on failure */
--- 426,452 ----
  	save_time = loc ? pstrdup(loc) : NULL;
  #endif
  
! 
! 	elog(NOTICE,"starting init_interp");
! 	plperl_held_interp = perl_alloc();
! 	if (!plperl_held_interp)
  		elog(ERROR, "could not allocate Perl interpreter");
  
! 	perl_construct(plperl_held_interp);
! 	perl_parse(plperl_held_interp, plperl_init_shared_libs, 
! 			   3, embedding, NULL);
! 	perl_run(plperl_held_interp);
  
! 	if (interp_state == INTERP_NONE)
! 	{
! 		SV *res;
  
+ 		res = eval_pv(TEST_FOR_MULTI,TRUE);
+ 		can_run_two = SvIV(res); 
+ 		interp_state = INTERP_HELD;
+ 	}
+ 
+ 	elog(NOTICE,"leaving init_interp");
  #ifdef WIN32
  
  	eval_pv("use POSIX qw(locale_h);", TRUE);	/* croak on failure */
***************
*** 753,758 ****
--- 904,913 ----
  	SV		   *subref;
  	int			count;
  	char	   *compile_sub;
+ 	bool       oldcontext = trusted_context;
+ 
+ 	elog(NOTICE,"starting create_sub");
+ 	check_interp(trusted);
  
  	if (trusted && !plperl_safe_init_done)
  	{
***************
*** 828,833 ****
--- 983,991 ----
  	FREETMPS;
  	LEAVE;
  
+ 	restore_context(oldcontext);
+ 	elog(NOTICE,"leaving create_sub");
+ 
  	return subref;
  }
  
***************
*** 1009,1015 ****
--- 1167,1175 ----
  	Datum		retval;
  	ReturnSetInfo *rsi;
  	SV		   *array_ret = NULL;
+ 	bool       oldcontext = trusted_context;
  
+ 	elog(NOTICE,"starting plperl_func_handler");
  	/*
  	 * Create the call_data beforing connecting to SPI, so that it is not
  	 * allocated in the SPI memory context
***************
*** 1037,1042 ****
--- 1197,1204 ----
  							"cannot accept a set")));
  	}
  
+ 	check_interp(prodesc->lanpltrusted);
+ 
  	perlret = plperl_call_perl_func(prodesc, fcinfo);
  
  	/************************************************************
***************
*** 1146,1151 ****
--- 1308,1316 ----
  		SvREFCNT_dec(perlret);
  
  	current_call_data = NULL;
+ 	restore_context(oldcontext);
+ 	elog(NOTICE,"leaving plperl__func_handler");
+ 
  	return retval;
  }
  
***************
*** 1158,1163 ****
--- 1323,1329 ----
  	Datum		retval;
  	SV		   *svTD;
  	HV		   *hvTD;
+ 	bool       oldcontext = trusted_context;
  
  	/*
  	 * Create the call_data beforing connecting to SPI, so that it is not
***************
*** 1174,1179 ****
--- 1340,1347 ----
  	prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, true);
  	current_call_data->prodesc = prodesc;
  
+ 	check_interp(prodesc->lanpltrusted);
+ 
  	svTD = plperl_trigger_build_args(fcinfo);
  	perlret = plperl_call_perl_trigger_func(prodesc, fcinfo, svTD);
  	hvTD = (HV *) SvRV(svTD);
***************
*** 1244,1249 ****
--- 1412,1418 ----
  		SvREFCNT_dec(perlret);
  
  	current_call_data = NULL;
+ 	restore_context(oldcontext);
  	return retval;
  }
  
***************
*** 1256,1262 ****
  	char		internal_proname[64];
  	plperl_proc_desc *prodesc = NULL;
  	int			i;
! 	SV		  **svp;
  
  	/* We'll need the pg_proc tuple in any case... */
  	procTup = SearchSysCache(PROCOID,
--- 1425,1434 ----
  	char		internal_proname[64];
  	plperl_proc_desc *prodesc = NULL;
  	int			i;
! 	plperl_proc_entry *hash_entry;
! 	bool found;
! 
! 	elog(NOTICE,"starting compile_plperl_function");
  
  	/* We'll need the pg_proc tuple in any case... */
  	procTup = SearchSysCache(PROCOID,
***************
*** 1274,1288 ****
  	else
  		sprintf(internal_proname, "__PLPerl_proc_%u_trigger", fn_oid);
  
  	/************************************************************
  	 * Lookup the internal proc name in the hashtable
  	 ************************************************************/
! 	svp = hv_fetch_string(plperl_proc_hash, internal_proname);
! 	if (svp)
  	{
  		bool		uptodate;
  
! 		prodesc = INT2PTR(plperl_proc_desc *, SvUV(*svp));
  
  		/************************************************************
  		 * If it's present, must check whether it's still up to date.
--- 1446,1466 ----
  	else
  		sprintf(internal_proname, "__PLPerl_proc_%u_trigger", fn_oid);
  
+ 	elog(NOTICE,"looking for function");
  	/************************************************************
  	 * Lookup the internal proc name in the hashtable
  	 ************************************************************/
! 	hash_entry = hash_search(plperl_proc_hash, internal_proname, 
! 							 HASH_FIND, NULL);
! 	elog(NOTICE,"lookup finished");
! 
! 	if (hash_entry)
  	{
  		bool		uptodate;
  
! 		elog(NOTICE,"function exists");
! 
! 		prodesc = hash_entry->proc_data;
  
  		/************************************************************
  		 * If it's present, must check whether it's still up to date.
***************
*** 1294,1301 ****
  
  		if (!uptodate)
  		{
! 			/* need we delete old entry? */
  			prodesc = NULL;
  		}
  	}
  
--- 1472,1485 ----
  
  		if (!uptodate)
  		{
! 			elog(NOTICE,"function out of date ... removing");
! 
! 			free(prodesc); /* are we leaking memory here? */
  			prodesc = NULL;
+ 			hash_search(plperl_proc_hash, internal_proname,
+ 						HASH_REMOVE,NULL);
+ 			elog(NOTICE,"removal complete");
+ 	
  		}
  	}
  
***************
*** 1469,1474 ****
--- 1653,1660 ----
  		/************************************************************
  		 * Create the procedure in the interpreter
  		 ************************************************************/
+ 		elog(NOTICE,"creating subroutine");
+ 
  		prodesc->reference = plperl_create_sub(proc_source, prodesc->lanpltrusted);
  		pfree(proc_source);
  		if (!prodesc->reference)	/* can this happen? */
***************
*** 1479,1490 ****
  				 internal_proname);
  		}
  
! 		hv_store_string(plperl_proc_hash, internal_proname,
! 						newSVuv(PTR2UV(prodesc)));
  	}
  
  	ReleaseSysCache(procTup);
  
  	return prodesc;
  }
  
--- 1665,1681 ----
  				 internal_proname);
  		}
  
! 		elog(NOTICE,"storing new entry");
! 
! 		hash_entry = hash_search(plperl_proc_hash, internal_proname,
! 								 HASH_ENTER, &found);
! 		hash_entry->proc_data = prodesc;
  	}
  
  	ReleaseSysCache(procTup);
  
+ 	elog(NOTICE,"leaving compile_plperl_func");
+ 
  	return prodesc;
  }
  
***************
*** 1939,1944 ****
--- 2130,2137 ----
  plperl_spi_prepare(char *query, int argc, SV **argv)
  {
  	plperl_query_desc *qdesc;
+ 	plperl_query_entry *hash_entry;
+ 	bool        found;
  	void	   *plan;
  	int			i;
  
***************
*** 2051,2057 ****
  	 * Insert a hashtable entry for the plan and return
  	 * the key to the caller.
  	 ************************************************************/
! 	hv_store_string(plperl_query_hash, qdesc->qname, newSVuv(PTR2UV(qdesc)));
  
  	return newSVstring(qdesc->qname);
  }
--- 2244,2253 ----
  	 * Insert a hashtable entry for the plan and return
  	 * the key to the caller.
  	 ************************************************************/
! 
! 	hash_entry = hash_search(plperl_query_hash, qdesc->qname,
! 							 HASH_ENTER,&found);
! 	hash_entry->query_data = qdesc;
  
  	return newSVstring(qdesc->qname);
  }
***************
*** 2067,2072 ****
--- 2263,2269 ----
  	char	   *nulls;
  	Datum	   *argvalues;
  	plperl_query_desc *qdesc;
+ 	plperl_query_entry *hash_entry;
  
  	/*
  	 * Execute the query inside a sub-transaction, so we can cope with errors
***************
*** 2084,2096 ****
  		/************************************************************
  		 * Fetch the saved plan descriptor, see if it's o.k.
  		 ************************************************************/
! 		sv = hv_fetch_string(plperl_query_hash, query);
! 		if (sv == NULL)
  			elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
- 		if (*sv == NULL || !SvOK(*sv))
- 			elog(ERROR, "spi_exec_prepared: panic - plperl_query_hash value corrupted");
  
! 		qdesc = INT2PTR(plperl_query_desc *, SvUV(*sv));
  		if (qdesc == NULL)
  			elog(ERROR, "spi_exec_prepared: panic - plperl_query_hash value vanished");
  
--- 2281,2294 ----
  		/************************************************************
  		 * Fetch the saved plan descriptor, see if it's o.k.
  		 ************************************************************/
! 
! 		hash_entry = hash_search(plperl_query_hash, query,
! 										 HASH_FIND,NULL);
! 		if (hash_entry == NULL)
  			elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
  
! 		qdesc = hash_entry->query_data;
! 
  		if (qdesc == NULL)
  			elog(ERROR, "spi_exec_prepared: panic - plperl_query_hash value vanished");
  
***************
*** 2201,2211 ****
  SV *
  plperl_spi_query_prepared(char *query, int argc, SV **argv)
  {
- 	SV		  **sv;
  	int			i;
  	char	   *nulls;
  	Datum	   *argvalues;
  	plperl_query_desc *qdesc;
  	SV		   *cursor;
  	Portal		portal = NULL;
  
--- 2399,2409 ----
  SV *
  plperl_spi_query_prepared(char *query, int argc, SV **argv)
  {
  	int			i;
  	char	   *nulls;
  	Datum	   *argvalues;
  	plperl_query_desc *qdesc;
+ 	plperl_query_entry *hash_entry;
  	SV		   *cursor;
  	Portal		portal = NULL;
  
***************
*** 2225,2237 ****
  		/************************************************************
  		 * Fetch the saved plan descriptor, see if it's o.k.
  		 ************************************************************/
! 		sv = hv_fetch_string(plperl_query_hash, query);
! 		if (sv == NULL)
! 			elog(ERROR, "spi_query_prepared: Invalid prepared query passed");
! 		if (*sv == NULL || !SvOK(*sv))
! 			elog(ERROR, "spi_query_prepared: panic - plperl_query_hash value corrupted");
  
- 		qdesc = INT2PTR(plperl_query_desc *, SvUV(*sv));
  		if (qdesc == NULL)
  			elog(ERROR, "spi_query_prepared: panic - plperl_query_hash value vanished");
  
--- 2423,2435 ----
  		/************************************************************
  		 * Fetch the saved plan descriptor, see if it's o.k.
  		 ************************************************************/
! 		hash_entry = hash_search(plperl_query_hash, query,
! 										 HASH_FIND,NULL);
! 		if (hash_entry == NULL)
! 			elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
! 
! 		qdesc = hash_entry->query_data;
  
  		if (qdesc == NULL)
  			elog(ERROR, "spi_query_prepared: panic - plperl_query_hash value vanished");
  
***************
*** 2335,2351 ****
  void
  plperl_spi_freeplan(char *query)
  {
- 	SV		  **sv;
  	void	   *plan;
  	plperl_query_desc *qdesc;
  
! 	sv = hv_fetch_string(plperl_query_hash, query);
! 	if (sv == NULL)
! 		elog(ERROR, "spi_exec_freeplan: Invalid prepared query passed");
! 	if (*sv == NULL || !SvOK(*sv))
! 		elog(ERROR, "spi_exec_freeplan: panic - plperl_query_hash value corrupted");
  
- 	qdesc = INT2PTR(plperl_query_desc *, SvUV(*sv));
  	if (qdesc == NULL)
  		elog(ERROR, "spi_exec_freeplan: panic - plperl_query_hash value vanished");
  
--- 2533,2549 ----
  void
  plperl_spi_freeplan(char *query)
  {
  	void	   *plan;
  	plperl_query_desc *qdesc;
+ 	plperl_query_entry *hash_entry;
  
! 	hash_entry = hash_search(plperl_query_hash, query,
! 										 HASH_FIND,NULL);
! 	if (hash_entry == NULL)
! 		elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
! 
! 	qdesc = hash_entry->query_data;
  
  	if (qdesc == NULL)
  		elog(ERROR, "spi_exec_freeplan: panic - plperl_query_hash value vanished");
  
***************
*** 2353,2359 ****
  	 * free all memory before SPI_freeplan, so if it dies, nothing will be
  	 * left over
  	 */
! 	hv_delete(plperl_query_hash, query, strlen(query), G_DISCARD);
  	plan = qdesc->plan;
  	free(qdesc->argtypes);
  	free(qdesc->arginfuncs);
--- 2551,2559 ----
  	 * free all memory before SPI_freeplan, so if it dies, nothing will be
  	 * left over
  	 */
! 	hash_search(plperl_query_hash, query, 
! 				HASH_REMOVE,NULL);
! 
  	plan = qdesc->plan;
  	free(qdesc->argtypes);
  	free(qdesc->arginfuncs);
#2Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#1)
1 attachment(s)
Re: WIP 2 interpreters for plperl

I wrote:

I have made some progress with what I think is needed to have two
interpreters for plperl. This is a lot harder than the pltcl case for
two reasons: 1. there are no restrictions on having 2 tcl
interpreters, and 2. tcl does not need to save and restore context as
we have to do with perl. I think I have a conceptual siolution to
these two problems, but what I have is currently segfaulting somewhat
myteriously. Tracing a dynamically loaded library in a postgres
backend with a debugger is less than fun, too. I am attaching what I
currently have, liberally sprinkled with elog(NOTICE) calls as trace
writes.

With a little more perseverance I found the problem. The attached patch
passes regression. But it now needs plenty of eyeballs and testing.

cheers

andrew

Attachments:

perldifftext/plain; name=perldiffDownload
Index: plperl.c
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plperl/plperl.c,v
retrieving revision 1.121
diff -c -r1.121 plperl.c
*** plperl.c	19 Oct 2006 18:32:47 -0000	1.121
--- plperl.c	5 Nov 2006 22:20:16 -0000
***************
*** 27,32 ****
--- 27,33 ----
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  #include "utils/typcache.h"
+ #include "utils/hsearch.h"
  
  /* perl stuff */
  #include "plperl.h"
***************
*** 55,60 ****
--- 56,69 ----
  	SV		   *reference;
  } plperl_proc_desc;
  
+ /* hash table entry for proc desc  */
+ 
+ typedef struct plperl_proc_entry
+ {
+ 	char proc_name[NAMEDATALEN];
+ 	plperl_proc_desc *proc_data;
+ } plperl_proc_entry;
+ 
  /*
   * The information we cache for the duration of a single call to a
   * function.
***************
*** 82,94 ****
  	Oid		   *argtypioparams;
  } plperl_query_desc;
  
  /**********************************************************************
   * Global data
   **********************************************************************/
  static bool plperl_safe_init_done = false;
! static PerlInterpreter *plperl_interp = NULL;
! static HV  *plperl_proc_hash = NULL;
! static HV  *plperl_query_hash = NULL;
  
  static bool plperl_use_strict = false;
  
--- 91,128 ----
  	Oid		   *argtypioparams;
  } plperl_query_desc;
  
+ /* hash table entry for query desc  */
+ 
+ typedef struct plperl_query_entry
+ {
+ 	char query_name[NAMEDATALEN];
+ 	plperl_query_desc *query_data;
+ } 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 bool can_run_two = false;
+ 
  static bool plperl_safe_init_done = false;
! static PerlInterpreter *plperl_trusted_interp = NULL;
! static PerlInterpreter *plperl_untrusted_interp = NULL;
! static PerlInterpreter *plperl_held_interp = NULL;
! static bool can_run_two;
! static bool trusted_context;
! static HTAB  *plperl_proc_hash = NULL;
! static HTAB  *plperl_query_hash = NULL;
  
  static bool plperl_use_strict = false;
  
***************
*** 144,149 ****
--- 178,184 ----
  {
  	/* Be sure we do initialization only once (should be redundant now) */
  	static bool inited = false;
+     HASHCTL     hash_ctl;
  
  	if (inited)
  		return;
***************
*** 157,162 ****
--- 192,213 ----
  
  	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",
+ 								   32,
+ 								   &hash_ctl,
+ 								   HASH_ELEM);
+ 
+ 	hash_ctl.entrysize = sizeof(plperl_query_entry);
+ 	plperl_query_hash = hash_create("PLPerl Queries",
+ 									32,
+ 									&hash_ctl,
+ 									HASH_ELEM);
+ 
  	plperl_init_interp();
  
  	inited = true;
***************
*** 235,240 ****
--- 286,375 ----
  	"      elog(ERROR,'trusted Perl functions disabled - " \
  	"      please upgrade Perl Safe module to version 2.09 or later');}]); }"
  
+ #define TEST_FOR_MULTI \
+ 	"use Config; " \
+ 	"$Config{usemultiplicity} eq 'define' or "  \
+     "($Config{usethreads} eq 'define' " \
+ 	" and $Config{useithreads} eq 'define')"
+ 
+ 
+ /********************************************************************
+  *
+  * 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.
+  * We detect if it is safe to run two interpreters during the setup of the
+  * dummy interpreter.
+  */
+ 
+ 
+ static void 
+ check_interp(bool trusted)
+ {
+ 	if (interp_state == INTERP_HELD)
+ 	{
+ 		if (trusted)
+ 		{
+ 			plperl_trusted_interp = plperl_held_interp;
+ 			interp_state = INTERP_TRUSTED;
+ 		}
+ 		else
+ 		{
+ 			plperl_untrusted_interp = plperl_held_interp;
+ 			interp_state = INTERP_UNTRUSTED;
+ 		}
+ 		plperl_held_interp = NULL;
+ 		trusted_context = trusted;
+ 	}
+ 	else if (interp_state == INTERP_BOTH || 
+ 			 (trusted && interp_state == INTERP_TRUSTED) ||
+ 			 (!trusted && interp_state == INTERP_UNTRUSTED))
+ 	{
+ 		if (trusted_context != trusted)
+ 		{
+ 			if (trusted)
+ 				PERL_SET_CONTEXT(plperl_trusted_interp);
+ 			else
+ 				PERL_SET_CONTEXT(plperl_untrusted_interp);
+ 			trusted_context = trusted;
+ 		}
+ 	}
+ 	else if (can_run_two)
+ 	{
+ 		PERL_SET_CONTEXT(plperl_held_interp);
+ 		plperl_init_interp();
+ 		if (trusted)
+ 			plperl_trusted_interp = plperl_held_interp;
+ 		else
+ 			plperl_untrusted_interp = plperl_held_interp;
+ 		interp_state = INTERP_BOTH;
+ 		plperl_held_interp = NULL;
+ 		trusted_context = trusted;
+ 	}
+ 	else
+ 	{
+ 		elog(ERROR, 
+ 			 "can not allocate second Perl interpreter on this platform");
+ 
+ 	}
+ 	
+ }
+ 
+ 
+ static void
+ restore_context (bool old_context)
+ {
+ 	if (trusted_context != old_context)
+ 	{
+ 		if (old_context)
+ 			PERL_SET_CONTEXT(plperl_trusted_interp);
+ 		else
+ 			PERL_SET_CONTEXT(plperl_untrusted_interp);
+ 		trusted_context = old_context;
+ 	}
+ }
  
  static void
  plperl_init_interp(void)
***************
*** 285,300 ****
  	save_time = loc ? pstrdup(loc) : NULL;
  #endif
  
- 	plperl_interp = perl_alloc();
- 	if (!plperl_interp)
- 		elog(ERROR, "could not allocate Perl interpreter");
  
! 	perl_construct(plperl_interp);
! 	perl_parse(plperl_interp, plperl_init_shared_libs, 3, embedding, NULL);
! 	perl_run(plperl_interp);
  
! 	plperl_proc_hash = newHV();
! 	plperl_query_hash = newHV();
  
  #ifdef WIN32
  
--- 420,443 ----
  	save_time = loc ? pstrdup(loc) : NULL;
  #endif
  
  
! 	plperl_held_interp = perl_alloc();
! 	if (!plperl_held_interp)
! 		elog(ERROR, "could not allocate Perl interpreter");
  
! 	perl_construct(plperl_held_interp);
! 	perl_parse(plperl_held_interp, plperl_init_shared_libs, 
! 			   3, embedding, NULL);
! 	perl_run(plperl_held_interp);
! 
! 	if (interp_state == INTERP_NONE)
! 	{
! 		SV *res;
! 
! 		res = eval_pv(TEST_FOR_MULTI,TRUE);
! 		can_run_two = SvIV(res); 
! 		interp_state = INTERP_HELD;
! 	}
  
  #ifdef WIN32
  
***************
*** 1009,1014 ****
--- 1152,1158 ----
  	Datum		retval;
  	ReturnSetInfo *rsi;
  	SV		   *array_ret = NULL;
+ 	bool       oldcontext = trusted_context;
  
  	/*
  	 * Create the call_data beforing connecting to SPI, so that it is not
***************
*** 1037,1042 ****
--- 1181,1188 ----
  							"cannot accept a set")));
  	}
  
+ 	check_interp(prodesc->lanpltrusted);
+ 
  	perlret = plperl_call_perl_func(prodesc, fcinfo);
  
  	/************************************************************
***************
*** 1146,1151 ****
--- 1292,1299 ----
  		SvREFCNT_dec(perlret);
  
  	current_call_data = NULL;
+ 	restore_context(oldcontext);
+ 
  	return retval;
  }
  
***************
*** 1158,1163 ****
--- 1306,1312 ----
  	Datum		retval;
  	SV		   *svTD;
  	HV		   *hvTD;
+ 	bool       oldcontext = trusted_context;
  
  	/*
  	 * Create the call_data beforing connecting to SPI, so that it is not
***************
*** 1174,1179 ****
--- 1323,1330 ----
  	prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, true);
  	current_call_data->prodesc = prodesc;
  
+ 	check_interp(prodesc->lanpltrusted);
+ 
  	svTD = plperl_trigger_build_args(fcinfo);
  	perlret = plperl_call_perl_trigger_func(prodesc, fcinfo, svTD);
  	hvTD = (HV *) SvRV(svTD);
***************
*** 1244,1249 ****
--- 1395,1401 ----
  		SvREFCNT_dec(perlret);
  
  	current_call_data = NULL;
+ 	restore_context(oldcontext);
  	return retval;
  }
  
***************
*** 1256,1262 ****
  	char		internal_proname[64];
  	plperl_proc_desc *prodesc = NULL;
  	int			i;
! 	SV		  **svp;
  
  	/* We'll need the pg_proc tuple in any case... */
  	procTup = SearchSysCache(PROCOID,
--- 1408,1416 ----
  	char		internal_proname[64];
  	plperl_proc_desc *prodesc = NULL;
  	int			i;
! 	plperl_proc_entry *hash_entry;
! 	bool found;
! 	bool oldcontext = trusted_context;
  
  	/* We'll need the pg_proc tuple in any case... */
  	procTup = SearchSysCache(PROCOID,
***************
*** 1277,1288 ****
  	/************************************************************
  	 * Lookup the internal proc name in the hashtable
  	 ************************************************************/
! 	svp = hv_fetch_string(plperl_proc_hash, internal_proname);
! 	if (svp)
  	{
  		bool		uptodate;
  
! 		prodesc = INT2PTR(plperl_proc_desc *, SvUV(*svp));
  
  		/************************************************************
  		 * If it's present, must check whether it's still up to date.
--- 1431,1444 ----
  	/************************************************************
  	 * Lookup the internal proc name in the hashtable
  	 ************************************************************/
! 	hash_entry = hash_search(plperl_proc_hash, internal_proname, 
! 							 HASH_FIND, NULL);
! 
! 	if (hash_entry)
  	{
  		bool		uptodate;
  
! 		prodesc = hash_entry->proc_data;
  
  		/************************************************************
  		 * If it's present, must check whether it's still up to date.
***************
*** 1294,1301 ****
  
  		if (!uptodate)
  		{
! 			/* need we delete old entry? */
  			prodesc = NULL;
  		}
  	}
  
--- 1450,1459 ----
  
  		if (!uptodate)
  		{
! 			free(prodesc); /* are we leaking memory here? */
  			prodesc = NULL;
+ 			hash_search(plperl_proc_hash, internal_proname,
+ 						HASH_REMOVE,NULL);
  		}
  	}
  
***************
*** 1469,1475 ****
--- 1627,1639 ----
  		/************************************************************
  		 * Create the procedure in the interpreter
  		 ************************************************************/
+ 
+ 		check_interp(prodesc->lanpltrusted);
+ 
  		prodesc->reference = plperl_create_sub(proc_source, prodesc->lanpltrusted);
+ 
+ 		restore_context(oldcontext);
+ 
  		pfree(proc_source);
  		if (!prodesc->reference)	/* can this happen? */
  		{
***************
*** 1479,1486 ****
  				 internal_proname);
  		}
  
! 		hv_store_string(plperl_proc_hash, internal_proname,
! 						newSVuv(PTR2UV(prodesc)));
  	}
  
  	ReleaseSysCache(procTup);
--- 1643,1651 ----
  				 internal_proname);
  		}
  
! 		hash_entry = hash_search(plperl_proc_hash, internal_proname,
! 								 HASH_ENTER, &found);
! 		hash_entry->proc_data = prodesc;
  	}
  
  	ReleaseSysCache(procTup);
***************
*** 1939,1944 ****
--- 2104,2111 ----
  plperl_spi_prepare(char *query, int argc, SV **argv)
  {
  	plperl_query_desc *qdesc;
+ 	plperl_query_entry *hash_entry;
+ 	bool        found;
  	void	   *plan;
  	int			i;
  
***************
*** 2051,2057 ****
  	 * Insert a hashtable entry for the plan and return
  	 * the key to the caller.
  	 ************************************************************/
! 	hv_store_string(plperl_query_hash, qdesc->qname, newSVuv(PTR2UV(qdesc)));
  
  	return newSVstring(qdesc->qname);
  }
--- 2218,2227 ----
  	 * Insert a hashtable entry for the plan and return
  	 * the key to the caller.
  	 ************************************************************/
! 
! 	hash_entry = hash_search(plperl_query_hash, qdesc->qname,
! 							 HASH_ENTER,&found);
! 	hash_entry->query_data = qdesc;
  
  	return newSVstring(qdesc->qname);
  }
***************
*** 2067,2072 ****
--- 2237,2243 ----
  	char	   *nulls;
  	Datum	   *argvalues;
  	plperl_query_desc *qdesc;
+ 	plperl_query_entry *hash_entry;
  
  	/*
  	 * Execute the query inside a sub-transaction, so we can cope with errors
***************
*** 2084,2096 ****
  		/************************************************************
  		 * Fetch the saved plan descriptor, see if it's o.k.
  		 ************************************************************/
! 		sv = hv_fetch_string(plperl_query_hash, query);
! 		if (sv == NULL)
  			elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
- 		if (*sv == NULL || !SvOK(*sv))
- 			elog(ERROR, "spi_exec_prepared: panic - plperl_query_hash value corrupted");
  
! 		qdesc = INT2PTR(plperl_query_desc *, SvUV(*sv));
  		if (qdesc == NULL)
  			elog(ERROR, "spi_exec_prepared: panic - plperl_query_hash value vanished");
  
--- 2255,2268 ----
  		/************************************************************
  		 * Fetch the saved plan descriptor, see if it's o.k.
  		 ************************************************************/
! 
! 		hash_entry = hash_search(plperl_query_hash, query,
! 										 HASH_FIND,NULL);
! 		if (hash_entry == NULL)
  			elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
  
! 		qdesc = hash_entry->query_data;
! 
  		if (qdesc == NULL)
  			elog(ERROR, "spi_exec_prepared: panic - plperl_query_hash value vanished");
  
***************
*** 2201,2211 ****
  SV *
  plperl_spi_query_prepared(char *query, int argc, SV **argv)
  {
- 	SV		  **sv;
  	int			i;
  	char	   *nulls;
  	Datum	   *argvalues;
  	plperl_query_desc *qdesc;
  	SV		   *cursor;
  	Portal		portal = NULL;
  
--- 2373,2383 ----
  SV *
  plperl_spi_query_prepared(char *query, int argc, SV **argv)
  {
  	int			i;
  	char	   *nulls;
  	Datum	   *argvalues;
  	plperl_query_desc *qdesc;
+ 	plperl_query_entry *hash_entry;
  	SV		   *cursor;
  	Portal		portal = NULL;
  
***************
*** 2225,2237 ****
  		/************************************************************
  		 * Fetch the saved plan descriptor, see if it's o.k.
  		 ************************************************************/
! 		sv = hv_fetch_string(plperl_query_hash, query);
! 		if (sv == NULL)
! 			elog(ERROR, "spi_query_prepared: Invalid prepared query passed");
! 		if (*sv == NULL || !SvOK(*sv))
! 			elog(ERROR, "spi_query_prepared: panic - plperl_query_hash value corrupted");
  
- 		qdesc = INT2PTR(plperl_query_desc *, SvUV(*sv));
  		if (qdesc == NULL)
  			elog(ERROR, "spi_query_prepared: panic - plperl_query_hash value vanished");
  
--- 2397,2409 ----
  		/************************************************************
  		 * Fetch the saved plan descriptor, see if it's o.k.
  		 ************************************************************/
! 		hash_entry = hash_search(plperl_query_hash, query,
! 										 HASH_FIND,NULL);
! 		if (hash_entry == NULL)
! 			elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
! 
! 		qdesc = hash_entry->query_data;
  
  		if (qdesc == NULL)
  			elog(ERROR, "spi_query_prepared: panic - plperl_query_hash value vanished");
  
***************
*** 2335,2351 ****
  void
  plperl_spi_freeplan(char *query)
  {
- 	SV		  **sv;
  	void	   *plan;
  	plperl_query_desc *qdesc;
  
! 	sv = hv_fetch_string(plperl_query_hash, query);
! 	if (sv == NULL)
! 		elog(ERROR, "spi_exec_freeplan: Invalid prepared query passed");
! 	if (*sv == NULL || !SvOK(*sv))
! 		elog(ERROR, "spi_exec_freeplan: panic - plperl_query_hash value corrupted");
  
- 	qdesc = INT2PTR(plperl_query_desc *, SvUV(*sv));
  	if (qdesc == NULL)
  		elog(ERROR, "spi_exec_freeplan: panic - plperl_query_hash value vanished");
  
--- 2507,2523 ----
  void
  plperl_spi_freeplan(char *query)
  {
  	void	   *plan;
  	plperl_query_desc *qdesc;
+ 	plperl_query_entry *hash_entry;
  
! 	hash_entry = hash_search(plperl_query_hash, query,
! 										 HASH_FIND,NULL);
! 	if (hash_entry == NULL)
! 		elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
! 
! 	qdesc = hash_entry->query_data;
  
  	if (qdesc == NULL)
  		elog(ERROR, "spi_exec_freeplan: panic - plperl_query_hash value vanished");
  
***************
*** 2353,2359 ****
  	 * free all memory before SPI_freeplan, so if it dies, nothing will be
  	 * left over
  	 */
! 	hv_delete(plperl_query_hash, query, strlen(query), G_DISCARD);
  	plan = qdesc->plan;
  	free(qdesc->argtypes);
  	free(qdesc->arginfuncs);
--- 2525,2533 ----
  	 * free all memory before SPI_freeplan, so if it dies, nothing will be
  	 * left over
  	 */
! 	hash_search(plperl_query_hash, query, 
! 				HASH_REMOVE,NULL);
! 
  	plan = qdesc->plan;
  	free(qdesc->argtypes);
  	free(qdesc->arginfuncs);
#3Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#2)
Re: [PATCHES] WIP 2 interpreters for plperl

[moving to -hackers]

I wrote:

I have made some progress with what I think is needed to have two
interpreters for plperl. This is a lot harder than the pltcl case for
two reasons: 1. there are no restrictions on having 2 tcl
interpreters, and 2. tcl does not need to save and restore context as
we have to do with perl. I think I have a conceptual siolution to
these two problems, but what I have is currently segfaulting somewhat
myteriously. Tracing a dynamically loaded library in a postgres
backend with a debugger is less than fun, too. I am attaching what I
currently have, liberally sprinkled with elog(NOTICE) calls as trace
writes.

With a little more perseverance I found the problem. The attached
patch passes regression. But it now needs plenty of eyeballs and testing.

Well, if anyone cast eyeballs over it they kept it secret from me :-(

However, I have now tested the patch with the little script shown below
and it seems to do the Right Thing (tm) in switching context and
restoring it. So I think it can be applied to HEAD, along with an
addition to the docs and a release note.

Since this is a behaviour modification, do we want to apply it to the
back branches? Doing so would certainly be possible, although it would
be non-trivial.

cheers

andrew

------------

drop function if exists f1(int);
drop function if exists f2(int);

create function f1(int) returns void language plperl as
$$

my $arg = shift;
elog NOTICE,"in plperl func f1($arg)";
return if ($arg > 5);
$arg++;
spi_exec_query("select f2($arg)");

$$;

create function f2(int) returns void language plperlu as
$$

my $arg = shift;
elog NOTICE,"in plperlu func f2($arg)";
return if ($arg > 5);
$arg++;
spi_exec_query("select f1($arg)");

$$;

select f1(0);
select f2(0);

#4Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#3)
Re: [PATCHES] WIP 2 interpreters for plperl

I wrote:

[moving to -hackers]

I wrote:

I have made some progress with what I think is needed to have two
interpreters for plperl. This is a lot harder than the pltcl case
for two reasons: 1. there are no restrictions on having 2 tcl
interpreters, and 2. tcl does not need to save and restore context
as we have to do with perl. I think I have a conceptual siolution to
these two problems, but what I have is currently segfaulting
somewhat myteriously. Tracing a dynamically loaded library in a
postgres backend with a debugger is less than fun, too. I am
attaching what I currently have, liberally sprinkled with
elog(NOTICE) calls as trace writes.

With a little more perseverance I found the problem. The attached
patch passes regression. But it now needs plenty of eyeballs and
testing.

Well, if anyone cast eyeballs over it they kept it secret from me :-(

However, I have now tested the patch with the little script shown
below and it seems to do the Right Thing (tm) in switching context and
restoring it. So I think it can be applied to HEAD, along with an
addition to the docs and a release note.

Since this is a behaviour modification, do we want to apply it to the
back branches? Doing so would certainly be possible, although it would
be non-trivial.

I have committed this to HEAD at any rate, so that we can get some
buildfarm testing going.

cheers

andrew

#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Dunstan (#4)
Re: [PATCHES] WIP 2 interpreters for plperl

Andrew Dunstan <andrew@dunslane.net> writes:

Since this is a behaviour modification, do we want to apply it to the
back branches? Doing so would certainly be possible, although it would
be non-trivial.

I have committed this to HEAD at any rate, so that we can get some
buildfarm testing going.

My vote is to leave it just in HEAD; there may be someone out there
depending on plperl and plperlu being in the same interpreter, and
breaking their code in a minor release doesn't seem very friendly.

regards, tom lane

#6Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#5)
Re: [PATCHES] WIP 2 interpreters for plperl

Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

Since this is a behaviour modification, do we want to apply it to the
back branches? Doing so would certainly be possible, although it would
be non-trivial.

I have committed this to HEAD at any rate, so that we can get some
buildfarm testing going.

My vote is to leave it just in HEAD; there may be someone out there
depending on plperl and plperlu being in the same interpreter, and
breaking their code in a minor release doesn't seem very friendly.

Fine by me.

cheers

andrew

#7Jim Buttafuoco
jim@contactbda.com
In reply to: Tom Lane (#5)
Re: [PATCHES] WIP 2 interpreters for plperl

I might be one of the ones who depends on the same interpreter. In your new
scheme, the _SHARED hash will only be shared between like interpreters,
correct? This is going to force me to switch all of my perl code to use the
plperlu interpreter :(

-----Original Message-----
From: pgsql-hackers-owner@postgresql.org
[mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Tom Lane
Sent: Monday, November 20, 2006 2:10 PM
To: Andrew Dunstan
Cc: PostgreSQL-development
Subject: Re: [HACKERS] [PATCHES] WIP 2 interpreters for plperl

Andrew Dunstan <andrew@dunslane.net> writes:

Since this is a behaviour modification, do we want to apply it to the
back branches? Doing so would certainly be possible, although it would
be non-trivial.

I have committed this to HEAD at any rate, so that we can get some
buildfarm testing going.

My vote is to leave it just in HEAD; there may be someone out there
depending on plperl and plperlu being in the same interpreter, and
breaking their code in a minor release doesn't seem very friendly.

regards, tom lane

---------------------------(end of broadcast)---------------------------
TIP 1: if posting/reading through Usenet, please send an appropriate
subscribe-nomail command to majordomo@postgresql.org so that your
message can get through to the mailing list cleanly

#8Andrew Dunstan
andrew@dunslane.net
In reply to: Jim Buttafuoco (#7)
Re: [PATCHES] WIP 2 interpreters for plperl

Jim Buttafuoco wrote:

I might be one of the ones who depends on the same interpreter. In your new
scheme, the _SHARED hash will only be shared between like interpreters,
correct? This is going to force me to switch all of my perl code to use the
plperlu interpreter :(

Yes. Sorry, but I can't see any way around it. If anyone can suggest one
then speak up loudly ASAP.

cheers

andrew

#9Martijn van Oosterhout
kleptog@svana.org
In reply to: Andrew Dunstan (#8)
Re: [PATCHES] WIP 2 interpreters for plperl

On Mon, Nov 20, 2006 at 04:14:34PM -0500, Andrew Dunstan wrote:

Jim Buttafuoco wrote:

I might be one of the ones who depends on the same interpreter. In your
new
scheme, the _SHARED hash will only be shared between like interpreters,
correct? This is going to force me to switch all of my perl code to use
the
plperlu interpreter :(

Yes. Sorry, but I can't see any way around it. If anyone can suggest one
then speak up loudly ASAP.

Since the stuff plperlu should be small and self contained, you just
need to set it up so all the data needed by the plperlu function is
passed as a parameter. I suppose we'd need to look at the use case to
see if this is a real obsticle.

I suppose you're not permitted to call other perl functions directly
with \%_SHARED as a parameter, right?

Have a nice day,
--
Martijn van Oosterhout <kleptog@svana.org> http://svana.org/kleptog/

Show quoted text

From each according to his ability. To each according to his ability to litigate.

#10Andrew Dunstan
andrew@dunslane.net
In reply to: Martijn van Oosterhout (#9)
Re: [PATCHES] WIP 2 interpreters for plperl

Martijn van Oosterhout wrote:

On Mon, Nov 20, 2006 at 04:14:34PM -0500, Andrew Dunstan wrote:

Jim Buttafuoco wrote:

I might be one of the ones who depends on the same interpreter. In your
new
scheme, the _SHARED hash will only be shared between like interpreters,
correct? This is going to force me to switch all of my perl code to use
the
plperlu interpreter :(

Yes. Sorry, but I can't see any way around it. If anyone can suggest one
then speak up loudly ASAP.

Since the stuff plperlu should be small and self contained, you just
need to set it up so all the data needed by the plperlu function is
passed as a parameter. I suppose we'd need to look at the use case to
see if this is a real obsticle.

I suppose you're not permitted to call other perl functions directly
with \%_SHARED as a parameter, right?

\%_SHARED only has meaning in the context of a given perl interpreter.
If we use it in another interpreter it will point to the middle of
nowhere. It's the equivalent of one program passing a pointer to another
program. I thought of playing clever games with a tied interface
(perldoc perltie for more info), but then we'd still have troubles with
things like:

my $xxx=2;
$_SHARED{foo} = { bar => [1,2,3], baz=> sub { return ++$xxx; } };

The only thing I have seen that looked remotely promising is the
non-standard Safe::World module, which if it lives up to its promise
might allow us to go back to using a single interpreter. But I have not
had time to investigate further, and I don't thing we can rely on a
module almost no standard installation will have, unless we want to ship
it ourselves. It doesn't seem to have been worked on since 2004. It is
certainly too late to think of anything like that for 8.2, I think - it
would need significant analysis and testing which I do not currently
have time for, and release is just around the corner, we fervently hope.

cheers

andrew