src/pl/tcl/pltcl.c
static Datum
pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
				   bool pltrusted)
{
	bool		nonatomic;
	pltcl_proc_desc *prodesc;
	Tcl_Interp *volatile interp;
	Tcl_Obj    *tcl_cmd;
	int			i;
	int			tcl_rc;
	Datum		retval;

	nonatomic = fcinfo->context &&
		IsA(fcinfo->context, CallContext) &&
		!castNode(CallContext, fcinfo->context)->atomic;

	/* Connect to SPI manager */
	if (SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0) != SPI_OK_CONNECT)
		elog(ERROR, "could not connect to SPI manager");

	/* Find or compile the function */
	prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
									 false, pltrusted);

	call_state->prodesc = prodesc;
	prodesc->fn_refcount++;

	interp = prodesc->interp_desc->interp;

	/*
	 * If we're a SRF, check caller can handle materialize mode, and save
	 * relevant info into call_state.  We must ensure that the returned
	 * tuplestore is owned by the caller's context, even if we first create it
	 * inside a subtransaction.
	 */
	if (prodesc->fn_retisset)
	{
		ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;

		if (!rsi || !IsA(rsi, ReturnSetInfo) ||
			(rsi->allowedModes & SFRM_Materialize) == 0)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("set-valued function called in context that cannot accept a set")));

		call_state->rsi = rsi;
		call_state->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory;
		call_state->tuple_store_owner = CurrentResourceOwner;
	}

	/************************************************************
	 * Create the tcl command to call the internal
	 * proc in the Tcl interpreter
	 ************************************************************/
	tcl_cmd = Tcl_NewObj();
	Tcl_ListObjAppendElement(NULL, tcl_cmd,
							 Tcl_NewStringObj(prodesc->internal_proname, -1));

	/* We hold a refcount on tcl_cmd just to be sure it stays around */
	Tcl_IncrRefCount(tcl_cmd);

	/************************************************************
	 * Add all call arguments to the command
	 ************************************************************/
	PG_TRY();
	{
		for (i = 0; i < prodesc->nargs; i++)
		{
			if (prodesc->arg_is_rowtype[i])
			{
				/**************************************************
				 * For tuple values, add a list for 'array set ...'
				 **************************************************/
				if (fcinfo->args[i].isnull)
					Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
				else
				{
					HeapTupleHeader td;
					Oid			tupType;
					int32		tupTypmod;
					TupleDesc	tupdesc;
					HeapTupleData tmptup;
					Tcl_Obj    *list_tmp;

					td = DatumGetHeapTupleHeader(fcinfo->args[i].value);
					/* Extract rowtype info and find a tupdesc */
					tupType = HeapTupleHeaderGetTypeId(td);
					tupTypmod = HeapTupleHeaderGetTypMod(td);
					tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
					/* Build a temporary HeapTuple control structure */
					tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
					tmptup.t_data = td;

					list_tmp = pltcl_build_tuple_argument(&tmptup, tupdesc, true);
					Tcl_ListObjAppendElement(NULL, tcl_cmd, list_tmp);

					ReleaseTupleDesc(tupdesc);
				}
			}
			else
			{
				/**************************************************
				 * Single values are added as string element
				 * of their external representation
				 **************************************************/
				if (fcinfo->args[i].isnull)
					Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
				else
				{
					char	   *tmp;

					tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
											 fcinfo->args[i].value);
					UTF_BEGIN;
					Tcl_ListObjAppendElement(NULL, tcl_cmd,
											 Tcl_NewStringObj(UTF_E2U(tmp), -1));
					UTF_END;
					pfree(tmp);
				}
			}
		}
	}
	PG_CATCH();
	{
		/* Release refcount to free tcl_cmd */
		Tcl_DecrRefCount(tcl_cmd);
		PG_RE_THROW();
	}
	PG_END_TRY();

	/************************************************************
	 * Call the Tcl function
	 *
	 * We assume no PG error can be thrown directly from this call.
	 ************************************************************/
	tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));

	/* Release refcount to free tcl_cmd (and all subsidiary objects) */
	Tcl_DecrRefCount(tcl_cmd);

	/************************************************************
	 * Check for errors reported by Tcl.
	 ************************************************************/
	if (tcl_rc != TCL_OK)
		throw_tcl_error(interp, prodesc->user_proname);

	/************************************************************
	 * Disconnect from SPI manager and then create the return
	 * value 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).  But don't try to call
	 * the result_in_func if we've been told to return a NULL;
	 * the Tcl result may not be a valid value of the result type
	 * in that case.
	 ************************************************************/
	if (SPI_finish() != SPI_OK_FINISH)
		elog(ERROR, "SPI_finish() failed");

	if (prodesc->fn_retisset)
	{
		ReturnSetInfo *rsi = call_state->rsi;

		/* We already checked this is OK */
		rsi->returnMode = SFRM_Materialize;

		/* If we produced any tuples, send back the result */
		if (call_state->tuple_store)
		{
			rsi->setResult = call_state->tuple_store;
			if (call_state->ret_tupdesc)
			{
				MemoryContext oldcxt;

				oldcxt = MemoryContextSwitchTo(call_state->tuple_store_cxt);
				rsi->setDesc = CreateTupleDescCopy(call_state->ret_tupdesc);
				MemoryContextSwitchTo(oldcxt);
			}
		}
		retval = (Datum) 0;
		fcinfo->isnull = true;
	}
	else if (fcinfo->isnull)
	{
		retval = InputFunctionCall(&prodesc->result_in_func,
								   NULL,
								   prodesc->result_typioparam,
								   -1);
	}
	else if (prodesc->fn_retistuple)
	{
		TupleDesc	td;
		HeapTuple	tup;
		Tcl_Obj    *resultObj;
		Tcl_Obj   **resultObjv;
		int			resultObjc;

		/*
		 * Set up data about result type.  XXX it's tempting to consider
		 * caching this in the prodesc, in the common case where the rowtype
		 * is determined by the function not the calling query.  But we'd have
		 * to be able to deal with ADD/DROP/ALTER COLUMN events when the
		 * result type is a named composite type, so it's not exactly trivial.
		 * Maybe worth improving someday.
		 */
		switch (get_call_result_type(fcinfo, NULL, &td))
		{
			case TYPEFUNC_COMPOSITE:
				/* success */
				break;
			case TYPEFUNC_COMPOSITE_DOMAIN:
				Assert(prodesc->fn_retisdomain);
				break;
			case TYPEFUNC_RECORD:
				/* failed to determine actual type of RECORD */
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("function returning record called in context "
								"that cannot accept type record")));
				break;
			default:
				/* result type isn't composite? */
				elog(ERROR, "return type must be a row type");
				break;
		}

		Assert(!call_state->ret_tupdesc);
		Assert(!call_state->attinmeta);
		call_state->ret_tupdesc = td;
		call_state->attinmeta = TupleDescGetAttInMetadata(td);

		/* Convert function result to tuple */
		resultObj = Tcl_GetObjResult(interp);
		if (Tcl_ListObjGetElements(interp, resultObj, &resultObjc, &resultObjv) == TCL_ERROR)
			throw_tcl_error(interp, prodesc->user_proname);

		tup = pltcl_build_tuple_result(interp, resultObjv, resultObjc,
									   call_state);
		retval = HeapTupleGetDatum(tup);
	}
	else
		retval = InputFunctionCall(&prodesc->result_in_func,
								   utf_u2e(Tcl_GetStringResult(interp)),
								   prodesc->result_typioparam,
								   -1);

	return retval;
}
/**********************************************************************
 * pltcl_func_handler()		- Handler for regular function calls
 **********************************************************************/
pltcl_func_handler()         - Handler for regular function calls
pltcl_func_handler () - Handler for regular function calls
static Datum
pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
				   bool pltrusted)
{
	bool		nonatomic;
	pltcl_proc_desc *prodesc;
	Tcl_Interp *volatile interp;
	Tcl_Obj    *tcl_cmd;
	int			i;
	int			tcl_rc;
	Datum		retval;
	nonatomic = fcinfo->context &&
		IsA(fcinfo->context, CallContext) &&
		!castNode(CallContext, fcinfo->context)->atomic;
	if (SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0) != SPI_OK_CONNECT)
		elog(ERROR, "could not connect to SPI manager");
	prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
									 false, pltrusted);
	call_state->prodesc = prodesc;
	prodesc->fn_refcount++;
	interp = prodesc->interp_desc->interp;
	if (prodesc->fn_retisset)
	{
		ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
		if (!rsi || !IsA(rsi, ReturnSetInfo) ||
			(rsi->allowedModes & SFRM_Materialize) == 0)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("set-valued function called in context that cannot accept a set")));
		call_state->rsi = rsi;
		call_state->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory;
		call_state->tuple_store_owner = CurrentResourceOwner;
	}
	tcl_cmd = Tcl_NewObj();
	Tcl_ListObjAppendElement(NULL, tcl_cmd,
							 Tcl_NewStringObj(prodesc->internal_proname, -1));
	Tcl_IncrRefCount(tcl_cmd);
	PG_TRY();
	{
		for (i = 0; i < prodesc->nargs; i++)
		{
			if (prodesc->arg_is_rowtype[i])
			{
				if (fcinfo->args[i].isnull)
					Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
				else
				{
					HeapTupleHeader td;
					Oid			tupType;
					int32		tupTypmod;
					TupleDesc	tupdesc;
					HeapTupleData tmptup;
					Tcl_Obj    *list_tmp;
					td = DatumGetHeapTupleHeader(fcinfo->args[i].value);
					tupType = HeapTupleHeaderGetTypeId(td);
					tupTypmod = HeapTupleHeaderGetTypMod(td);
					tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
					tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
					tmptup.t_data = td;
					list_tmp = pltcl_build_tuple_argument(&tmptup, tupdesc, true);
					Tcl_ListObjAppendElement(NULL, tcl_cmd, list_tmp);
					ReleaseTupleDesc(tupdesc);
				}
			}
			else
			{
				if (fcinfo->args[i].isnull)
					Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
				else
				{
					char	   *tmp;
					tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
											 fcinfo->args[i].value);
					UTF_BEGIN;
					Tcl_ListObjAppendElement(NULL, tcl_cmd,
											 Tcl_NewStringObj(UTF_E2U(tmp), -1));
					UTF_END;
					pfree(tmp);
				}
			}
		}
	}
	PG_CATCH();
	{
		Tcl_DecrRefCount(tcl_cmd);
		PG_RE_THROW();
	}
	PG_END_TRY();
	tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
	Tcl_DecrRefCount(tcl_cmd);
	if (tcl_rc != TCL_OK)
		throw_tcl_error(interp, prodesc->user_proname);
	if (SPI_finish() != SPI_OK_FINISH)
		elog(ERROR, "SPI_finish() failed");
	if (prodesc->fn_retisset)
	{
		ReturnSetInfo *rsi = call_state->rsi;
		rsi->returnMode = SFRM_Materialize;
		if (call_state->tuple_store)
		{
			rsi->setResult = call_state->tuple_store;
			if (call_state->ret_tupdesc)
			{
				MemoryContext oldcxt;
				oldcxt = MemoryContextSwitchTo(call_state->tuple_store_cxt);
				rsi->setDesc = CreateTupleDescCopy(call_state->ret_tupdesc);
				MemoryContextSwitchTo(oldcxt);
			}
		}
		retval = (Datum) 0;
		fcinfo->isnull = true;
	}
	else if (fcinfo->isnull)
	{
		retval = InputFunctionCall(&prodesc->result_in_func,
								   NULL,
								   prodesc->result_typioparam,
								   -1);
	}
	else if (prodesc->fn_retistuple)
	{
		TupleDesc	td;
		HeapTuple	tup;
		Tcl_Obj    *resultObj;
		Tcl_Obj   **resultObjv;
		int			resultObjc;
		switch (get_call_result_type(fcinfo, NULL, &td))
		{
			case TYPEFUNC_COMPOSITE:
				break;
			case TYPEFUNC_COMPOSITE_DOMAIN:
				Assert(prodesc->fn_retisdomain);
				break;
			case TYPEFUNC_RECORD:
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("function returning record called in context "
								"that cannot accept type record")));
				break;
			default:
				elog(ERROR, "return type must be a row type");
				break;
		}
		Assert(!call_state->ret_tupdesc);
		Assert(!call_state->attinmeta);
		call_state->ret_tupdesc = td;
		call_state->attinmeta = TupleDescGetAttInMetadata(td);
		resultObj = Tcl_GetObjResult(interp);
		if (Tcl_ListObjGetElements(interp, resultObj, &resultObjc, &resultObjv) == TCL_ERROR)
			throw_tcl_error(interp, prodesc->user_proname);
		tup = pltcl_build_tuple_result(interp, resultObjv, resultObjc,
									   call_state);
		retval = HeapTupleGetDatum(tup);
	}
	else
		retval = InputFunctionCall(&prodesc->result_in_func,
								   utf_u2e(Tcl_GetStringResult(interp)),
								   prodesc->result_typioparam,
								   -1);
	return retval;
}
static Datum pltcl_func_handler ( PG_FUNCTION_ARGS , pltcl_call_state * call_state , bool pltrusted ) { bool nonatomic ; pltcl_proc_desc * prodesc ; Tcl_Interp * volatile interp ; Tcl_Obj * tcl_cmd ; int i ; int tcl_rc ; Datum retval ; nonatomic = fcinfo -> context && IsA ( fcinfo -> context , CallContext ) && ! castNode ( CallContext , fcinfo -> context ) -> atomic ; if ( SPI_connect_ext ( nonatomic ? SPI_OPT_NONATOMIC : 0 ) != SPI_OK_CONNECT ) elog ( ERROR , " " ) ; prodesc = compile_pltcl_function ( fcinfo -> flinfo -> fn_oid , InvalidOid , false , pltrusted ) ; call_state -> prodesc = prodesc ; prodesc -> fn_refcount ++ ; interp = prodesc -> interp_desc -> interp ; if ( prodesc -> fn_retisset ) { ReturnSetInfo * rsi = ( ReturnSetInfo * ) fcinfo -> resultinfo ; if ( ! rsi || ! IsA ( rsi , ReturnSetInfo ) || ( rsi -> allowedModes & SFRM_Materialize ) == 0 ) ereport ( ERROR , ( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) , errmsg ( " " ) ) ) ; call_state -> rsi = rsi ; call_state -> tuple_store_cxt = rsi -> econtext -> ecxt_per_query_memory ; call_state -> tuple_store_owner = CurrentResourceOwner ; } tcl_cmd = Tcl_NewObj ( ) ; Tcl_ListObjAppendElement ( NULL , tcl_cmd , Tcl_NewStringObj ( prodesc -> internal_proname , -1 ) ) ; Tcl_IncrRefCount ( tcl_cmd ) ; PG_TRY ( ) ; { for ( i = 0 ; i < prodesc -> nargs ; i ++ ) { if ( prodesc -> arg_is_rowtype [ i ] ) { if ( fcinfo -> args [ i ] . isnull ) Tcl_ListObjAppendElement ( NULL , tcl_cmd , Tcl_NewObj ( ) ) ; else { HeapTupleHeader td ; Oid tupType ; int32 tupTypmod ; TupleDesc tupdesc ; HeapTupleData tmptup ; Tcl_Obj * list_tmp ; td = DatumGetHeapTupleHeader ( fcinfo -> args [ i ] . value ) ; tupType = HeapTupleHeaderGetTypeId ( td ) ; tupTypmod = HeapTupleHeaderGetTypMod ( td ) ; tupdesc = lookup_rowtype_tupdesc ( tupType , tupTypmod ) ; tmptup . t_len = HeapTupleHeaderGetDatumLength ( td ) ; tmptup . t_data = td ; list_tmp = pltcl_build_tuple_argument ( & tmptup , tupdesc , true ) ; Tcl_ListObjAppendElement ( NULL , tcl_cmd , list_tmp ) ; ReleaseTupleDesc ( tupdesc ) ; } } else { if ( fcinfo -> args [ i ] . isnull ) Tcl_ListObjAppendElement ( NULL , tcl_cmd , Tcl_NewObj ( ) ) ; else { char * tmp ; tmp = OutputFunctionCall ( & prodesc -> arg_out_func [ i ] , fcinfo -> args [ i ] . value ) ; UTF_BEGIN ; Tcl_ListObjAppendElement ( NULL , tcl_cmd , Tcl_NewStringObj ( UTF_E2U ( tmp ) , -1 ) ) ; UTF_END ; pfree ( tmp ) ; } } } } PG_CATCH ( ) ; { Tcl_DecrRefCount ( tcl_cmd ) ; PG_RE_THROW ( ) ; } PG_END_TRY ( ) ; tcl_rc = Tcl_EvalObjEx ( interp , tcl_cmd , ( TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL ) ) ; Tcl_DecrRefCount ( tcl_cmd ) ; if ( tcl_rc != TCL_OK ) throw_tcl_error ( interp , prodesc -> user_proname ) ; if ( SPI_finish ( ) != SPI_OK_FINISH ) elog ( ERROR , " " ) ; if ( prodesc -> fn_retisset ) { ReturnSetInfo * rsi = call_state -> rsi ; rsi -> returnMode = SFRM_Materialize ; if ( call_state -> tuple_store ) { rsi -> setResult = call_state -> tuple_store ; if ( call_state -> ret_tupdesc ) { MemoryContext oldcxt ; oldcxt = MemoryContextSwitchTo ( call_state -> tuple_store_cxt ) ; rsi -> setDesc = CreateTupleDescCopy ( call_state -> ret_tupdesc ) ; MemoryContextSwitchTo ( oldcxt ) ; } } retval = ( Datum ) 0 ; fcinfo -> isnull = true ; } else if ( fcinfo -> isnull ) { retval = InputFunctionCall ( & prodesc -> result_in_func , NULL , prodesc -> result_typioparam , -1 ) ; } else if ( prodesc -> fn_retistuple ) { TupleDesc td ; HeapTuple tup ; Tcl_Obj * resultObj ; Tcl_Obj * * resultObjv ; int resultObjc ; switch ( get_call_result_type ( fcinfo , NULL , & td ) ) { case TYPEFUNC_COMPOSITE : break ; case TYPEFUNC_COMPOSITE_DOMAIN : Assert ( prodesc -> fn_retisdomain ) ; break ; case TYPEFUNC_RECORD : ereport ( ERROR , ( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) , errmsg ( " " " " ) ) ) ; break ; default : elog ( ERROR , " " ) ; break ; } Assert ( ! call_state -> ret_tupdesc ) ; Assert ( ! call_state -> attinmeta ) ; call_state -> ret_tupdesc = td ; call_state -> attinmeta = TupleDescGetAttInMetadata ( td ) ; resultObj = Tcl_GetObjResult ( interp ) ; if ( Tcl_ListObjGetElements ( interp , resultObj , & resultObjc , & resultObjv ) == TCL_ERROR ) throw_tcl_error ( interp , prodesc -> user_proname ) ; tup = pltcl_build_tuple_result ( interp , resultObjv , resultObjc , call_state ) ; retval = HeapTupleGetDatum ( tup ) ; } else retval = InputFunctionCall ( & prodesc -> result_in_func , utf_u2e ( Tcl_GetStringResult ( interp ) ) , prodesc -> result_typioparam , -1 ) ; return retval ; }
pltcl_func_handler()         - Handler for regular function calls
pltcl_func_handler () - Handler for regular function calls
/* Connect to SPI manager */ /* Find or compile the function */ /*
	 * If we're a SRF, check caller can handle materialize mode, and save
	 * relevant info into call_state.  We must ensure that the returned
	 * tuplestore is owned by the caller's context, even if we first create it
	 * inside a subtransaction.
	 */ /************************************************************
	 * Create the tcl command to call the internal
	 * proc in the Tcl interpreter
	 ************************************************************/ /* We hold a refcount on tcl_cmd just to be sure it stays around */ /************************************************************
	 * Add all call arguments to the command
	 ************************************************************/ /**************************************************
				 * For tuple values, add a list for 'array set ...'
				 **************************************************/ /* Extract rowtype info and find a tupdesc */ /* Build a temporary HeapTuple control structure */ /**************************************************
				 * Single values are added as string element
				 * of their external representation
				 **************************************************/ /* Release refcount to free tcl_cmd */ /************************************************************
	 * Call the Tcl function
	 *
	 * We assume no PG error can be thrown directly from this call.
	 ************************************************************/ /* Release refcount to free tcl_cmd (and all subsidiary objects) */ /************************************************************
	 * Check for errors reported by Tcl.
	 ************************************************************/ /************************************************************
	 * Disconnect from SPI manager and then create the return
	 * value 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).  But don't try to call
	 * the result_in_func if we've been told to return a NULL;
	 * the Tcl result may not be a valid value of the result type
	 * in that case.
	 ************************************************************/ /* We already checked this is OK */ /* If we produced any tuples, send back the result */ /*
		 * Set up data about result type.  XXX it's tempting to consider
		 * caching this in the prodesc, in the common case where the rowtype
		 * is determined by the function not the calling query.  But we'd have
		 * to be able to deal with ADD/DROP/ALTER COLUMN events when the
		 * result type is a named composite type, so it's not exactly trivial.
		 * Maybe worth improving someday.
		 */ /* success */ /* failed to determine actual type of RECORD */ /* result type isn't composite? */ /* Convert function result to tuple */