⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 plperl.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 4 页
字号:
/********************************************************************** * plperl.c - perl as a procedural language for PostgreSQL * * IDENTIFICATION * *	  This software is copyrighted by Mark Hollomon *	  but is shameless cribbed from pltcl.c by Jan Wieck. * *	  The author hereby grants permission  to  use,  copy,	modify, *	  distribute,  and	license this software and its documentation *	  for any purpose, provided that existing copyright notices are *	  retained	in	all  copies  and  that	this notice is included *	  verbatim in any distributions. No written agreement, license, *	  or  royalty  fee	is required for any of the authorized uses. *	  Modifications to this software may be  copyrighted  by  their *	  author  and  need  not  follow  the licensing terms described *	  here, provided that the new terms are  clearly  indicated  on *	  the first page of each file where they apply. * *	  IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY *	  PARTY  FOR  DIRECT,	INDIRECT,	SPECIAL,   INCIDENTAL,	 OR *	  CONSEQUENTIAL   DAMAGES  ARISING	OUT  OF  THE  USE  OF  THIS *	  SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN *	  IF  THE  AUTHOR  HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH *	  DAMAGE. * *	  THE  AUTHOR  AND	DISTRIBUTORS  SPECIFICALLY	 DISCLAIM	ANY *	  WARRANTIES,  INCLUDING,  BUT	NOT  LIMITED  TO,  THE	IMPLIED *	  WARRANTIES  OF  MERCHANTABILITY,	FITNESS  FOR  A  PARTICULAR *	  PURPOSE,	AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON *	  AN "AS IS" BASIS, AND THE AUTHOR	AND  DISTRIBUTORS  HAVE  NO *	  OBLIGATION   TO	PROVIDE   MAINTENANCE,	 SUPPORT,  UPDATES, *	  ENHANCEMENTS, OR MODIFICATIONS. * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.94.2.5 2006/01/28 16:21:33 adunstan Exp $ * **********************************************************************/#include "postgres.h"/* Defined by Perl */#undef _/* system stuff */#include <ctype.h>#include <fcntl.h>#include <unistd.h>#include <locale.h>/* postgreSQL stuff */#include "commands/trigger.h"#include "executor/spi.h"#include "funcapi.h"#include "utils/lsyscache.h"#include "utils/memutils.h"#include "utils/typcache.h"#include "miscadmin.h"#include "mb/pg_wchar.h"/* perl stuff *//* stop perl from hijacking stdio and other stuff */#ifdef WIN32#define WIN32IO_IS_STDIO#endif #include "EXTERN.h"#include "perl.h"#include "XSUB.h"#include "ppport.h"#include "spi_internal.h"/* just in case these symbols aren't provided */#ifndef pTHX_#define pTHX_#define pTHX void#endif/********************************************************************** * The information we cache about loaded procedures **********************************************************************/typedef struct plperl_proc_desc{	char	   *proname;	TransactionId fn_xmin;	CommandId	fn_cmin;	bool		fn_readonly;	bool		lanpltrusted;	bool		fn_retistuple;	/* true, if function returns tuple */	bool		fn_retisset;	/* true, if function returns set */	bool		fn_retisarray;	/* true if function returns array */	Oid			result_oid;		/* Oid of result type */	FmgrInfo	result_in_func; /* I/O function and arg for result type */	Oid			result_typioparam;	int			nargs;	FmgrInfo	arg_out_func[FUNC_MAX_ARGS];	bool		arg_is_rowtype[FUNC_MAX_ARGS];	SV		   *reference;} plperl_proc_desc;/* * The information we cache for the duration of a single call to a * function. */typedef struct plperl_call_data{	plperl_proc_desc *prodesc;	FunctionCallInfo  fcinfo;	Tuplestorestate  *tuple_store;	TupleDesc		  ret_tdesc;	AttInMetadata	 *attinmeta;	MemoryContext	  tmp_cxt;} plperl_call_data;/********************************************************************** * Global data **********************************************************************/static bool plperl_firstcall = true;static bool plperl_safe_init_done = false;static PerlInterpreter *plperl_interp = NULL;static HV  *plperl_proc_hash = NULL;static bool plperl_use_strict = false;/* this is saved and restored by plperl_call_handler */static plperl_call_data *current_call_data = NULL;/********************************************************************** * Forward declarations **********************************************************************/static void plperl_init_all(void);static void plperl_init_interp(void);Datum		plperl_call_handler(PG_FUNCTION_ARGS);Datum		plperl_validator(PG_FUNCTION_ARGS);void		plperl_init(void);static Datum plperl_func_handler(PG_FUNCTION_ARGS);static Datum plperl_trigger_handler(PG_FUNCTION_ARGS);static plperl_proc_desc *compile_plperl_function(Oid fn_oid, bool is_trigger);static SV  *plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc);static void plperl_init_shared_libs(pTHX);static HV  *plperl_spi_execute_fetch_result(SPITupleTable *, int, int);/* * This routine is a crock, and so is everyplace that calls it.  The problem * is that the cached form of plperl functions/queries is allocated permanently * (mostly via malloc()) and never released until backend exit.  Subsidiary * data structures such as fmgr info records therefore must live forever * as well.  A better implementation would store all this stuff in a per- * function memory context that could be reclaimed at need.  In the meantime, * fmgr_info_cxt must be called specifying TopMemoryContext so that whatever * it might allocate, and whatever the eventual function might allocate using * fn_mcxt, will live forever too. */static voidperm_fmgr_info(Oid functionId, FmgrInfo *finfo){	fmgr_info_cxt(functionId, finfo, TopMemoryContext);}/* Perform initialization during postmaster startup. */voidplperl_init(void){	if (!plperl_firstcall)		return;	DefineCustomBoolVariable(							 "plperl.use_strict",	  "If true, will compile trusted and untrusted perl code in strict mode",							 NULL,							 &plperl_use_strict,							 PGC_USERSET,							 NULL, NULL);	EmitWarningsOnPlaceholders("plperl");	plperl_init_interp();	plperl_firstcall = false;}/* Perform initialization during backend startup. */static voidplperl_init_all(void){	if (plperl_firstcall)		plperl_init();	/* We don't need to do anything yet when a new backend starts. */}/* Each of these macros must represent a single string literal */#define PERLBOOT \	"SPI::bootstrap(); use vars qw(%_SHARED);" \	"sub ::plperl_warn { my $msg = shift; " \	"       $msg =~ s/\\(eval \\d+\\) //g; &elog(&NOTICE, $msg); } " \	"$SIG{__WARN__} = \\&::plperl_warn; " \	"sub ::plperl_die { my $msg = shift; " \	"       $msg =~ s/\\(eval \\d+\\) //g; die $msg; } " \	"$SIG{__DIE__} = \\&::plperl_die; " \	"sub ::mkunsafefunc {" \	"      my $ret = eval(qq[ sub { $_[0] $_[1] } ]); " \	"      $@ =~ s/\\(eval \\d+\\) //g if $@; return $ret; }" \	"use strict; " \	"sub ::mk_strict_unsafefunc {" \	"      my $ret = eval(qq[ sub { use strict; $_[0] $_[1] } ]); " \	"      $@ =~ s/\\(eval \\d+\\) //g if $@; return $ret; } " \	"sub ::_plperl_to_pg_array {" \	"  my $arg = shift; ref $arg eq 'ARRAY' || return $arg; " \	"  my $res = ''; my $first = 1; " \	"  foreach my $elem (@$arg) " \	"  { " \	"    $res .= ', ' unless $first; $first = undef; " \	"    if (ref $elem) " \	"    { " \	"      $res .= _plperl_to_pg_array($elem); " \	"    } " \	"    else " \	"    { " \	"      my $str = qq($elem); " \	"      $str =~ s/([\"\\\\])/\\\\$1/g; " \	"      $res .= qq(\"$str\"); " \	"    } " \	"  } " \	"  return qq({$res}); " \	"} "#define SAFE_MODULE \	"require Safe; $Safe::VERSION"#define SAFE_OK \	"use vars qw($PLContainer); $PLContainer = new Safe('PLPerl');" \	"$PLContainer->permit_only(':default');" \	"$PLContainer->permit(qw[:base_math !:base_io sort time]);" \	"$PLContainer->share(qw[&elog &spi_exec_query &return_next " \	"&spi_query &spi_fetchrow " \	"&_plperl_to_pg_array " \	"&DEBUG &LOG &INFO &NOTICE &WARNING &ERROR %_SHARED ]);" \	"sub ::mksafefunc {" \	"      my $ret = $PLContainer->reval(qq[sub { $_[0] $_[1] }]); " \	"      $@ =~ s/\\(eval \\d+\\) //g if $@; return $ret; }" \	"$PLContainer->permit('require'); $PLContainer->reval('use strict;');" \	"$PLContainer->deny('require');" \	"sub ::mk_strict_safefunc {" \	"      my $ret = $PLContainer->reval(qq[sub { BEGIN { strict->import(); } $_[0] $_[1] }]); " \	"      $@ =~ s/\\(eval \\d+\\) //g if $@; return $ret; }"#define SAFE_BAD \	"use vars qw($PLContainer); $PLContainer = new Safe('PLPerl');" \	"$PLContainer->permit_only(':default');" \	"$PLContainer->share(qw[&elog &ERROR ]);" \	"sub ::mksafefunc { return $PLContainer->reval(qq[sub { " \	"      elog(ERROR,'trusted Perl functions disabled - " \	"      please upgrade Perl Safe module to version 2.09 or later');}]); }" \	"sub ::mk_strict_safefunc { return $PLContainer->reval(qq[sub { " \	"      elog(ERROR,'trusted Perl functions disabled - " \	"      please upgrade Perl Safe module to version 2.09 or later');}]); }"static voidplperl_init_interp(void){	static char *embedding[3] = {		"", "-e", PERLBOOT	};#ifdef WIN32	/* 	 * The perl library on startup does horrible things like call	 * setlocale(LC_ALL,""). We have protected against that on most	 * platforms by setting the environment appropriately. However, on	 * Windows, setlocale() does not consult the environment, so we need	 * to save the existing locale settings before perl has a chance to 	 * mangle them and restore them after its dirty deeds are done.	 *	 * MSDN ref:	 * http://msdn.microsoft.com/library/en-us/vclib/html/_crt_locale.asp	 *	 * It appears that we only need to do this on interpreter startup, and	 * subsequent calls to the interpreter don't mess with the locale	 * settings.	 *	 * We restore them using Perl's POSIX::setlocale() function so that	 * Perl doesn't have a different idea of the locale from Postgres.	 *	 */	char *loc;	char *save_collate, *save_ctype, *save_monetary, *save_numeric, *save_time;	char buf[1024];	loc = setlocale(LC_COLLATE,NULL);	save_collate = loc ? pstrdup(loc) : NULL;	loc = setlocale(LC_CTYPE,NULL);	save_ctype = loc ? pstrdup(loc) : NULL;	loc = setlocale(LC_MONETARY,NULL);	save_monetary = loc ? pstrdup(loc) : NULL;	loc = setlocale(LC_NUMERIC,NULL);	save_numeric = loc ? pstrdup(loc) : NULL;	loc = setlocale(LC_TIME,NULL);	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();#ifdef WIN32	eval_pv("use POSIX qw(locale_h);", TRUE); /* croak on failure */	if (save_collate != NULL)	{		snprintf(buf, sizeof(buf),"setlocale(%s,'%s');",				 "LC_COLLATE",save_collate);		eval_pv(buf,TRUE);		pfree(save_collate);	}	if (save_ctype != NULL)	{		snprintf(buf, sizeof(buf),"setlocale(%s,'%s');",				 "LC_CTYPE",save_ctype);		eval_pv(buf,TRUE);		pfree(save_ctype);	}	if (save_monetary != NULL)	{		snprintf(buf, sizeof(buf),"setlocale(%s,'%s');",				 "LC_MONETARY",save_monetary);		eval_pv(buf,TRUE);		pfree(save_monetary);	}	if (save_numeric != NULL)	{		snprintf(buf, sizeof(buf),"setlocale(%s,'%s');",				 "LC_NUMERIC",save_numeric);		eval_pv(buf,TRUE);		pfree(save_numeric);	}	if (save_time != NULL)	{		snprintf(buf, sizeof(buf),"setlocale(%s,'%s');",				 "LC_TIME",save_time);		eval_pv(buf,TRUE);		pfree(save_time);	}#endif}static voidplperl_safe_init(void){	SV		   *res;	double		safe_version;	res = eval_pv(SAFE_MODULE, FALSE);	/* TRUE = croak if failure */	safe_version = SvNV(res);	/*	 * We actually want to reject safe_version < 2.09, but it's risky to	 * assume that floating-point comparisons are exact, so use a slightly	 * smaller comparison value.	 */	if (safe_version < 2.0899)	{		/* not safe, so disallow all trusted funcs */		eval_pv(SAFE_BAD, FALSE);	}	else	{		eval_pv(SAFE_OK, FALSE);	}	plperl_safe_init_done = true;}/* * Perl likes to put a newline after its error messages; clean up such */static char *strip_trailing_ws(const char *msg){	char	   *res = pstrdup(msg);	int			len = strlen(res);	while (len > 0 && isspace((unsigned char) res[len - 1]))		res[--len] = '\0';	return res;}/* Build a tuple from a hash. */static HeapTupleplperl_build_tuple_result(HV *perlhash, AttInMetadata *attinmeta){	TupleDesc	td = attinmeta->tupdesc;	char	  **values;	SV		   *val;	char	   *key;	I32			klen;	HeapTuple	tup;	values = (char **) palloc0(td->natts * sizeof(char *));	hv_iterinit(perlhash);	while ((val = hv_iternextsv(perlhash, &key, &klen)))	{		int			attn = SPI_fnumber(td, key);		if (attn <= 0 || td->attrs[attn - 1]->attisdropped)			ereport(ERROR,					(errcode(ERRCODE_UNDEFINED_COLUMN),					 errmsg("Perl hash contains nonexistent column \"%s\"",							key)));		if (SvOK(val) && SvTYPE(val) != SVt_NULL)			values[attn - 1] = SvPV(val, PL_na);	}	hv_iterinit(perlhash);	tup = BuildTupleFromCStrings(attinmeta, values);	pfree(values);	return tup;}/* * convert perl array to postgres string representation */static SV  *plperl_convert_to_pg_array(SV *src){	SV		   *rv;	int			count;	dSP;	PUSHMARK(SP);	XPUSHs(src);	PUTBACK;	count = call_pv("::_plperl_to_pg_array", G_SCALAR);	SPAGAIN;	if (count != 1)		elog(ERROR, "unexpected _plperl_to_pg_array failure");	rv = POPs;	PUTBACK;	return rv;}/* Set up the arguments for a trigger call. */static SV  *plperl_trigger_build_args(FunctionCallInfo fcinfo)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -