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

📄 plperl.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 4 页
字号:
	SPAGAIN;	if (count != 1)	{		PUTBACK;		FREETMPS;		LEAVE;		elog(ERROR, "didn't get a return item from trigger function");	}	if (SvTRUE(ERRSV))	{		(void) POPs;		PUTBACK;		FREETMPS;		LEAVE;		/* XXX need to find a way to assign an errcode here */		ereport(ERROR,				(errmsg("error from Perl trigger function: %s",						strip_trailing_ws(SvPV(ERRSV, PL_na)))));	}	retval = newSVsv(POPs);	PUTBACK;	FREETMPS;	LEAVE;	return retval;}static Datumplperl_func_handler(PG_FUNCTION_ARGS){	plperl_proc_desc *prodesc;	SV		   *perlret;	Datum		retval;	ReturnSetInfo *rsi;	SV		   *array_ret = NULL;	/*	 * Create the call_data beforing connecting to SPI, so that it is	 * not allocated in the SPI memory context	 */	current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));	current_call_data->fcinfo = fcinfo;	if (SPI_connect() != SPI_OK_CONNECT)		elog(ERROR, "could not connect to SPI manager");	prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false);	current_call_data->prodesc = prodesc;	rsi = (ReturnSetInfo *) fcinfo->resultinfo;	if (prodesc->fn_retisset)	{		/* Check context before allowing the call to go through */		if (!rsi || !IsA(rsi, ReturnSetInfo) ||			(rsi->allowedModes & SFRM_Materialize) == 0 ||			rsi->expectedDesc == NULL)			ereport(ERROR,					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),					 errmsg("set-valued function called in context that "							"cannot accept a set")));	}	perlret = plperl_call_perl_func(prodesc, fcinfo);	/************************************************************	 * Disconnect from SPI manager and then create the return	 * values datum (if the input function does a palloc for it	 * this must not be allocated in the SPI memory context	 * because SPI_finish would free it).	 ************************************************************/	if (SPI_finish() != SPI_OK_FINISH)		elog(ERROR, "SPI_finish() failed");	if (prodesc->fn_retisset)	{		/*		 * If the Perl function returned an arrayref, we pretend that it		 * called return_next() for each element of the array, to handle old		 * SRFs that didn't know about return_next(). Any other sort of return		 * value is an error.		 */		if (SvTYPE(perlret) == SVt_RV &&			SvTYPE(SvRV(perlret)) == SVt_PVAV)		{			int			i = 0;			SV		  **svp = 0;			AV		   *rav = (AV *) SvRV(perlret);			while ((svp = av_fetch(rav, i, FALSE)) != NULL)			{				plperl_return_next(*svp);				i++;			}		}		else if (SvTYPE(perlret) != SVt_NULL)		{			ereport(ERROR,					(errcode(ERRCODE_DATATYPE_MISMATCH),					 errmsg("set-returning Perl function must return "							"reference to array or use return_next")));		}		rsi->returnMode = SFRM_Materialize;		if (current_call_data->tuple_store)		{			rsi->setResult = current_call_data->tuple_store;			rsi->setDesc = current_call_data->ret_tdesc;		}		retval = (Datum) 0;	}	else if (SvTYPE(perlret) == SVt_NULL)	{		/* Return NULL if Perl code returned undef */		if (rsi && IsA(rsi, ReturnSetInfo))			rsi->isDone = ExprEndResult;		fcinfo->isnull = true;		retval = (Datum) 0;	}	else if (prodesc->fn_retistuple)	{		/* Return a perl hash converted to a Datum */		TupleDesc	td;		AttInMetadata *attinmeta;		HeapTuple	tup;		if (!SvOK(perlret) || SvTYPE(perlret) != SVt_RV ||			SvTYPE(SvRV(perlret)) != SVt_PVHV)		{			ereport(ERROR,					(errcode(ERRCODE_DATATYPE_MISMATCH),					 errmsg("composite-returning Perl function "							"must return reference to hash")));		}		/* XXX should cache the attinmeta data instead of recomputing */		if (get_call_result_type(fcinfo, NULL, &td) != TYPEFUNC_COMPOSITE)		{			ereport(ERROR,					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),					 errmsg("function returning record called in context "							"that cannot accept type record")));		}		attinmeta = TupleDescGetAttInMetadata(td);		tup = plperl_build_tuple_result((HV *) SvRV(perlret), attinmeta);		retval = HeapTupleGetDatum(tup);	}	else	{		/* Return a perl string converted to a Datum */		char	   *val;		if (prodesc->fn_retisarray && SvROK(perlret) &&			SvTYPE(SvRV(perlret)) == SVt_PVAV)		{			array_ret = plperl_convert_to_pg_array(perlret);			SvREFCNT_dec(perlret);			perlret = array_ret;		}		val = SvPV(perlret, PL_na);		retval = FunctionCall3(&prodesc->result_in_func,							   CStringGetDatum(val),							   ObjectIdGetDatum(prodesc->result_typioparam),							   Int32GetDatum(-1));	}	if (array_ret == NULL)		SvREFCNT_dec(perlret);	current_call_data = NULL;	return retval;}static Datumplperl_trigger_handler(PG_FUNCTION_ARGS){	plperl_proc_desc *prodesc;	SV		   *perlret;	Datum		retval;	SV		   *svTD;	HV		   *hvTD;	/*	 * Create the call_data beforing connecting to SPI, so that it is	 * not allocated in the SPI memory context	 */	current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));	current_call_data->fcinfo = fcinfo;	/* Connect to SPI manager */	if (SPI_connect() != SPI_OK_CONNECT)		elog(ERROR, "could not connect to SPI manager");	/* Find or compile the function */	prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, true);	current_call_data->prodesc = prodesc;	svTD = plperl_trigger_build_args(fcinfo);	perlret = plperl_call_perl_trigger_func(prodesc, fcinfo, svTD);	hvTD = (HV *) SvRV(svTD);	/************************************************************	* Disconnect from SPI manager and then create the return	* values datum (if the input function does a palloc for it	* this must not be allocated in the SPI memory context	* because SPI_finish would free it).	************************************************************/	if (SPI_finish() != SPI_OK_FINISH)		elog(ERROR, "SPI_finish() failed");	if (!(perlret && SvOK(perlret) && SvTYPE(perlret) != SVt_NULL))	{		/* undef result means go ahead with original tuple */		TriggerData *trigdata = ((TriggerData *) fcinfo->context);		if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))			retval = (Datum) trigdata->tg_trigtuple;		else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))			retval = (Datum) trigdata->tg_newtuple;		else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))			retval = (Datum) trigdata->tg_trigtuple;		else			retval = (Datum) 0; /* can this happen? */	}	else	{		HeapTuple	trv;		char	   *tmp;		tmp = SvPV(perlret, PL_na);		if (pg_strcasecmp(tmp, "SKIP") == 0)			trv = NULL;		else if (pg_strcasecmp(tmp, "MODIFY") == 0)		{			TriggerData *trigdata = (TriggerData *) fcinfo->context;			if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))				trv = plperl_modify_tuple(hvTD, trigdata,										  trigdata->tg_trigtuple);			else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))				trv = plperl_modify_tuple(hvTD, trigdata,										  trigdata->tg_newtuple);			else			{				ereport(WARNING,						(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),					   errmsg("ignoring modified tuple in DELETE trigger")));				trv = NULL;			}		}		else		{			ereport(ERROR,					(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),					 errmsg("result of Perl trigger function must be undef, "							"\"SKIP\" or \"MODIFY\"")));			trv = NULL;		}		retval = PointerGetDatum(trv);	}	SvREFCNT_dec(svTD);	if (perlret)		SvREFCNT_dec(perlret);	current_call_data = NULL;	return retval;}static plperl_proc_desc *compile_plperl_function(Oid fn_oid, bool is_trigger){	HeapTuple	procTup;	Form_pg_proc procStruct;	char		internal_proname[64];	int			proname_len;	plperl_proc_desc *prodesc = NULL;	int			i;	SV		  **svp;	/* We'll need the pg_proc tuple in any case... */	procTup = SearchSysCache(PROCOID,							 ObjectIdGetDatum(fn_oid),							 0, 0, 0);	if (!HeapTupleIsValid(procTup))		elog(ERROR, "cache lookup failed for function %u", fn_oid);	procStruct = (Form_pg_proc) GETSTRUCT(procTup);	/************************************************************	 * Build our internal proc name from the functions Oid	 ************************************************************/	if (!is_trigger)		sprintf(internal_proname, "__PLPerl_proc_%u", fn_oid);	else		sprintf(internal_proname, "__PLPerl_proc_%u_trigger", fn_oid);	proname_len = strlen(internal_proname);	/************************************************************	 * Lookup the internal proc name in the hashtable	 ************************************************************/	svp = hv_fetch(plperl_proc_hash, internal_proname, proname_len, FALSE);	if (svp)	{		bool		uptodate;		prodesc = (plperl_proc_desc *) SvIV(*svp);		/************************************************************		 * 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) &&				prodesc->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data));		if (!uptodate)		{			/* need we delete old entry? */			prodesc = NULL;		}	}	/************************************************************	 * If we haven't found it in the hashtable, we analyze	 * the functions arguments and returntype and store	 * the in-/out-functions in the prodesc block and create	 * a new hashtable entry for it.	 *	 * Then we load the procedure into the Perl interpreter.	 ************************************************************/	if (prodesc == NULL)	{		HeapTuple	langTup;		HeapTuple	typeTup;		Form_pg_language langStruct;		Form_pg_type typeStruct;		Datum		prosrcdatum;		bool		isnull;		char	   *proc_source;		/************************************************************		 * Allocate a new procedure description block		 ************************************************************/		prodesc = (plperl_proc_desc *) malloc(sizeof(plperl_proc_desc));		if (prodesc == NULL)			ereport(ERROR,					(errcode(ERRCODE_OUT_OF_MEMORY),					 errmsg("out of memory")));		MemSet(prodesc, 0, sizeof(plperl_proc_desc));		prodesc->proname = strdup(internal_proname);		prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);		prodesc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data);		/* Remember if function is STABLE/IMMUTABLE */		prodesc->fn_readonly =			(procStruct->provolatile != PROVOLATILE_VOLATILE);		/************************************************************		 * Lookup the pg_language tuple by Oid		 ************************************************************/		langTup = SearchSysCache(LANGOID,								 ObjectIdGetDatum(procStruct->prolang),								 0, 0, 0);		if (!HeapTupleIsValid(langTup))		{			free(prodesc->proname);			free(prodesc);			elog(ERROR, "cache lookup failed for language %u",				 procStruct->prolang);		}		langStruct = (Form_pg_language) GETSTRUCT(langTup);		prodesc->lanpltrusted = langStruct->lanpltrusted;		ReleaseSysCache(langTup);		/************************************************************		 * Get the required information for input conversion of the		 * return value.		 ************************************************************/		if (!is_trigger)		{			typeTup = SearchSysCache(TYPEOID,									 ObjectIdGetDatum(procStruct->prorettype),									 0, 0, 0);			if (!HeapTupleIsValid(typeTup))			{				free(prodesc->proname);				free(prodesc);				elog(ERROR, "cache lookup failed for type %u",					 procStruct->prorettype);			}			typeStruct = (Form_pg_type) GETSTRUCT(typeTup);			/* Disallow pseudotype result, except VOID or RECORD */			if (typeStruct->typtype == 'p')			{				if (procStruct->prorettype == VOIDOID ||					procStruct->prorettype == RECORDOID)					 /* okay */ ;				else if (procStruct->prorettype == TRIGGEROID)				{					free(prodesc->proname);					free(prodesc);					ereport(ERROR,							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),							 errmsg("trigger functions may only be called "									"as triggers")));				}				else				{					free(prodesc->proname);					free(prodesc);					ereport(ERROR,							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),							 errmsg("plperl functions cannot return type %s",									format_type_be(procStruct->prorettype))));				}			}			prodesc->result_oid = procStruct->prorettype;			prodesc->fn_retisset = procStruct->proretset;			prodesc->fn_retistuple = (typeStruct->typtype == 'c' ||									  procStruct->prorettype == RECORDOID);			prodesc->fn_retisarray =				(typeStruct->typlen == -1 && typeStruct->typelem);			perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func));			prodesc->result_typioparam = getTypeIOParam(typeTup);			ReleaseSysCache(typeTup);		}		/************************************************************		 * Get the required information for output conversion		 * of all procedure arguments		 ************************************************************/		if (!is_trigger)		{			prodesc->nargs = procStruct->pronargs;			for (i = 0; i < prodesc->nargs; i++)			{				typeTup = SearchSysCache(TYPEOID,						 ObjectIdGetDatum(procStruct->proargtypes.values[i]),										 0, 0, 0);				if (!HeapTupleIsValid(typeTup))				{					free(prodesc->proname);					free(prodesc);					elog(ERROR, "cache lookup failed for type %u",						 procStruct->proargtypes.values[i]);				}				typeStruct = (Form_pg_type) GETSTRUCT(typeTup);				/* Disallow pseudotype argument */				if (typeStruct->typtype == 'p')				{					free(prodesc->proname);					free(prodesc);					ereport(ERROR,							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),							 errmsg("plperl functions cannot take type %s",						format_type_be(procStruct->proargtypes.values[i]))));				}				if (typeStruct->typtype == 'c')					prodesc->arg_is_rowtype[i] = true;				else				{

⌨️ 快捷键说明

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