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

📄 functions.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
/*------------------------------------------------------------------------- * * functions.c *	  Execution of SQL-language functions * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.98.2.1 2005/11/22 18:23:08 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"#include "commands/trigger.h"#include "executor/executor.h"#include "executor/functions.h"#include "funcapi.h"#include "parser/parse_coerce.h"#include "parser/parse_expr.h"#include "parser/parse_type.h"#include "tcop/tcopprot.h"#include "tcop/utility.h"#include "utils/builtins.h"#include "utils/datum.h"#include "utils/lsyscache.h"#include "utils/syscache.h"#include "utils/typcache.h"/* * We have an execution_state record for each query in a function.	Each * record contains a querytree and plantree for its query.	If the query * is currently in F_EXEC_RUN state then there's a QueryDesc too. */typedef enum{	F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE} ExecStatus;typedef struct local_es{	struct local_es *next;	ExecStatus	status;	Query	   *query;	Plan	   *plan;	QueryDesc  *qd;				/* null unless status == RUN */} execution_state;#define LAST_POSTQUEL_COMMAND(es) ((es)->next == NULL)/* * An SQLFunctionCache record is built during the first call, * and linked to from the fn_extra field of the FmgrInfo struct. */typedef struct{	Oid		   *argtypes;		/* resolved types of arguments */	Oid			rettype;		/* actual return type */	int			typlen;			/* length of the return type */	bool		typbyval;		/* true if return type is pass by value */	bool		returnsTuple;	/* true if returning whole tuple result */	bool		shutdown_reg;	/* true if registered shutdown callback */	bool		readonly_func;	/* true to run in "read only" mode */	ParamListInfo paramLI;		/* Param list representing current args */	JunkFilter *junkFilter;		/* used only if returnsTuple */	/* head of linked list of execution_state records */	execution_state *func_state;} SQLFunctionCache;typedef SQLFunctionCache *SQLFunctionCachePtr;/* non-export function prototypes */static execution_state *init_execution_state(List *queryTree_list,					 bool readonly_func);static void init_sql_fcache(FmgrInfo *finfo);static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);static TupleTableSlot *postquel_getnext(execution_state *es);static void postquel_end(execution_state *es);static void postquel_sub_params(SQLFunctionCachePtr fcache,					FunctionCallInfo fcinfo);static Datum postquel_execute(execution_state *es,				 FunctionCallInfo fcinfo,				 SQLFunctionCachePtr fcache,				 MemoryContext resultcontext);static void sql_exec_error_callback(void *arg);static void ShutdownSQLFunction(Datum arg);static execution_state *init_execution_state(List *queryTree_list, bool readonly_func){	execution_state *firstes = NULL;	execution_state *preves = NULL;	ListCell   *qtl_item;	foreach(qtl_item, queryTree_list)	{		Query	   *queryTree = lfirst(qtl_item);		Plan	   *planTree;		execution_state *newes;		/* Precheck all commands for validity in a function */		if (queryTree->commandType == CMD_UTILITY &&			IsA(queryTree->utilityStmt, TransactionStmt))			ereport(ERROR,					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),			/* translator: %s is a SQL statement name */					 errmsg("%s is not allowed in a SQL function",							CreateQueryTag(queryTree))));		if (readonly_func && !QueryIsReadOnly(queryTree))			ereport(ERROR,					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),			/* translator: %s is a SQL statement name */					 errmsg("%s is not allowed in a non-volatile function",							CreateQueryTag(queryTree))));		planTree = pg_plan_query(queryTree, NULL);		newes = (execution_state *) palloc(sizeof(execution_state));		if (preves)			preves->next = newes;		else			firstes = newes;		newes->next = NULL;		newes->status = F_EXEC_START;		newes->query = queryTree;		newes->plan = planTree;		newes->qd = NULL;		preves = newes;	}	return firstes;}static voidinit_sql_fcache(FmgrInfo *finfo){	Oid			foid = finfo->fn_oid;	Oid			rettype;	HeapTuple	procedureTuple;	HeapTuple	typeTuple;	Form_pg_proc procedureStruct;	Form_pg_type typeStruct;	SQLFunctionCachePtr fcache;	Oid		   *argOidVect;	bool		haspolyarg;	char	   *src;	int			nargs;	List	   *queryTree_list;	Datum		tmp;	bool		isNull;	fcache = (SQLFunctionCachePtr) palloc0(sizeof(SQLFunctionCache));	/*	 * get the procedure tuple corresponding to the given function Oid	 */	procedureTuple = SearchSysCache(PROCOID,									ObjectIdGetDatum(foid),									0, 0, 0);	if (!HeapTupleIsValid(procedureTuple))		elog(ERROR, "cache lookup failed for function %u", foid);	procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);	/*	 * get the result type from the procedure tuple, and check for polymorphic	 * result type; if so, find out the actual result type.	 */	rettype = procedureStruct->prorettype;	if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)	{		rettype = get_fn_expr_rettype(finfo);		if (rettype == InvalidOid)		/* this probably should not happen */			ereport(ERROR,					(errcode(ERRCODE_DATATYPE_MISMATCH),					 errmsg("could not determine actual result type for function declared to return type %s",							format_type_be(procedureStruct->prorettype))));	}	fcache->rettype = rettype;	/* Remember if function is STABLE/IMMUTABLE */	fcache->readonly_func =		(procedureStruct->provolatile != PROVOLATILE_VOLATILE);	/* Now look up the actual result type */	typeTuple = SearchSysCache(TYPEOID,							   ObjectIdGetDatum(rettype),							   0, 0, 0);	if (!HeapTupleIsValid(typeTuple))		elog(ERROR, "cache lookup failed for type %u", rettype);	typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);	/*	 * get the type length and by-value flag from the type tuple; also do a	 * preliminary check for returnsTuple (this may prove inaccurate, see	 * below).	 */	fcache->typlen = typeStruct->typlen;	fcache->typbyval = typeStruct->typbyval;	fcache->returnsTuple = (typeStruct->typtype == 'c' ||							rettype == RECORDOID);	/*	 * Parse and rewrite the queries.  We need the argument type info to pass	 * to the parser.	 */	nargs = procedureStruct->pronargs;	haspolyarg = false;	if (nargs > 0)	{		int			argnum;		argOidVect = (Oid *) palloc(nargs * sizeof(Oid));		memcpy(argOidVect,			   procedureStruct->proargtypes.values,			   nargs * sizeof(Oid));		/* Resolve any polymorphic argument types */		for (argnum = 0; argnum < nargs; argnum++)		{			Oid			argtype = argOidVect[argnum];			if (argtype == ANYARRAYOID || argtype == ANYELEMENTOID)			{				argtype = get_fn_expr_argtype(finfo, argnum);				if (argtype == InvalidOid)					ereport(ERROR,							(errcode(ERRCODE_DATATYPE_MISMATCH),							 errmsg("could not determine actual type of argument declared %s",									format_type_be(argOidVect[argnum]))));				argOidVect[argnum] = argtype;				haspolyarg = true;			}		}	}	else		argOidVect = NULL;	fcache->argtypes = argOidVect;	tmp = SysCacheGetAttr(PROCOID,						  procedureTuple,						  Anum_pg_proc_prosrc,						  &isNull);	if (isNull)		elog(ERROR, "null prosrc for function %u", foid);	src = DatumGetCString(DirectFunctionCall1(textout, tmp));	queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);	/*	 * If the function has any arguments declared as polymorphic types, then	 * it wasn't type-checked at definition time; must do so now.	 *	 * Also, force a type-check if the declared return type is a rowtype; we	 * need to find out whether we are actually returning the whole tuple	 * result, or just regurgitating a rowtype expression result. In the	 * latter case we clear returnsTuple because we need not act different	 * from the scalar result case.	 *	 * In the returnsTuple case, check_sql_fn_retval will also construct a	 * JunkFilter we can use to coerce the returned rowtype to the desired	 * form.	 */	if (haspolyarg || fcache->returnsTuple)		fcache->returnsTuple = check_sql_fn_retval(foid,												   rettype,												   queryTree_list,												   &fcache->junkFilter);	/* Finally, plan the queries */	fcache->func_state = init_execution_state(queryTree_list,											  fcache->readonly_func);	pfree(src);	ReleaseSysCache(typeTuple);	ReleaseSysCache(procedureTuple);	finfo->fn_extra = (void *) fcache;}static voidpostquel_start(execution_state *es, SQLFunctionCachePtr fcache){	Snapshot	snapshot;	Assert(es->qd == NULL);	/*	 * In a read-only function, use the surrounding query's snapshot;	 * otherwise take a new snapshot for each query.  The snapshot should	 * include a fresh command ID so that all work to date in this transaction	 * is visible.	We copy in both cases so that postquel_end can	 * unconditionally do FreeSnapshot.	 */	if (fcache->readonly_func)		snapshot = CopySnapshot(ActiveSnapshot);	else	{		CommandCounterIncrement();		snapshot = CopySnapshot(GetTransactionSnapshot());	}	es->qd = CreateQueryDesc(es->query, es->plan,							 snapshot, InvalidSnapshot,							 None_Receiver,							 fcache->paramLI, false);	/* We assume we don't need to set up ActiveSnapshot for ExecutorStart */	/* Utility commands don't need Executor. */	if (es->qd->operation != CMD_UTILITY)	{		AfterTriggerBeginQuery();		ExecutorStart(es->qd, false);	}	es->status = F_EXEC_RUN;}static TupleTableSlot *postquel_getnext(execution_state *es){	TupleTableSlot *result;	Snapshot	saveActiveSnapshot;	long		count;	/* Make our snapshot the active one for any called functions */	saveActiveSnapshot = ActiveSnapshot;	PG_TRY();	{		ActiveSnapshot = es->qd->snapshot;		if (es->qd->operation == CMD_UTILITY)		{			ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->params,						   es->qd->dest, NULL);			result = NULL;		}		else		{			/*			 * If it's the function's last command, and it's a SELECT, fetch			 * one row at a time so we can return the results. Otherwise just			 * run it to completion.  (If we run to completion then			 * ExecutorRun is guaranteed to return NULL.)			 */			if (LAST_POSTQUEL_COMMAND(es) && es->qd->operation == CMD_SELECT)				count = 1L;			else				count = 0L;			result = ExecutorRun(es->qd, ForwardScanDirection, count);		}	}	PG_CATCH();	{		/* Restore global vars and propagate error */		ActiveSnapshot = saveActiveSnapshot;		PG_RE_THROW();	}	PG_END_TRY();	ActiveSnapshot = saveActiveSnapshot;	return result;}static voidpostquel_end(execution_state *es){	Snapshot	saveActiveSnapshot;	/* mark status done to ensure we don't do ExecutorEnd twice */	es->status = F_EXEC_DONE;	/* Utility commands don't need Executor. */	if (es->qd->operation != CMD_UTILITY)	{		/* Make our snapshot the active one for any called functions */		saveActiveSnapshot = ActiveSnapshot;		PG_TRY();		{			ActiveSnapshot = es->qd->snapshot;			AfterTriggerEndQuery(es->qd->estate);			ExecutorEnd(es->qd);		}		PG_CATCH();		{			/* Restore global vars and propagate error */			ActiveSnapshot = saveActiveSnapshot;			PG_RE_THROW();		}		PG_END_TRY();		ActiveSnapshot = saveActiveSnapshot;	}	FreeSnapshot(es->qd->snapshot);	FreeQueryDesc(es->qd);	es->qd = NULL;}/* Build ParamListInfo array representing current arguments */static voidpostquel_sub_params(SQLFunctionCachePtr fcache,					FunctionCallInfo fcinfo){	ParamListInfo paramLI;	int			nargs = fcinfo->nargs;	if (nargs > 0)	{		int			i;		paramLI = (ParamListInfo) palloc0((nargs + 1) * sizeof(ParamListInfoData));		for (i = 0; i < nargs; i++)		{			paramLI[i].kind = PARAM_NUM;			paramLI[i].id = i + 1;			paramLI[i].ptype = fcache->argtypes[i];			paramLI[i].value = fcinfo->arg[i];			paramLI[i].isnull = fcinfo->argnull[i];		}		paramLI[nargs].kind = PARAM_INVALID;	}	else		paramLI = NULL;	if (fcache->paramLI)		pfree(fcache->paramLI);	fcache->paramLI = paramLI;}static Datumpostquel_execute(execution_state *es,				 FunctionCallInfo fcinfo,				 SQLFunctionCachePtr fcache,				 MemoryContext resultcontext){	TupleTableSlot *slot;	Datum		value;	MemoryContext oldcontext;	if (es->status == F_EXEC_START)		postquel_start(es, fcache);	slot = postquel_getnext(es);	if (TupIsNull(slot))	{		/*		 * We fall out here for all cases except where we have obtained a row		 * from a function's final SELECT.		 */		postquel_end(es);		fcinfo->isnull = true;		return (Datum) NULL;	}	/*	 * If we got a row from a command within the function it has to be the	 * final command.  All others shouldn't be returning anything.	 */	Assert(LAST_POSTQUEL_COMMAND(es));	/*	 * Set up to return the function value.  For pass-by-reference datatypes,	 * be sure to allocate the result in resultcontext, not the current memory	 * context (which has query lifespan).	 */	oldcontext = MemoryContextSwitchTo(resultcontext);	if (fcache->returnsTuple)	{		/*		 * We are returning the whole tuple, so filter it and apply the proper		 * labeling to make it a valid Datum.  There are several reasons why		 * we do this:		 *		 * 1. To copy the tuple out of the child execution context and into		 * the desired result context.		 *		 * 2. To remove any junk attributes present in the raw subselect		 * result. (This is probably not absolutely necessary, but it seems		 * like good policy.)		 *		 * 3. To insert dummy null columns if the declared result type has any		 * attisdropped columns.		 */		HeapTuple	newtup;		HeapTupleHeader dtup;		uint32		t_len;		Oid			dtuptype;		int32		dtuptypmod;		newtup = ExecRemoveJunk(fcache->junkFilter, slot);		/*		 * Compress out the HeapTuple header data.	We assume that		 * heap_form_tuple made the tuple with header and body in one palloc'd		 * chunk.  We want to return a pointer to the chunk start so that it		 * will work if someone tries to free it.		 */		t_len = newtup->t_len;		dtup = (HeapTupleHeader) newtup;		memmove((char *) dtup, (char *) newtup->t_data, t_len);		/*		 * Use the declared return type if it's not RECORD; else take the type		 * from the computed result, making sure a typmod has been assigned.		 */		if (fcache->rettype != RECORDOID)		{			/* function has a named composite return type */			dtuptype = fcache->rettype;

⌨️ 快捷键说明

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