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

📄 functions.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 2 页
字号:
/*------------------------------------------------------------------------- * * functions.c *	  Execution of SQL-language functions * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.75.2.1 2004/09/06 18:23:09 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"#include "executor/execdefs.h"#include "executor/executor.h"#include "executor/functions.h"#include "tcop/pquery.h"#include "tcop/tcopprot.h"#include "tcop/utility.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/syscache.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 == (execution_state *) NULL)/* * An SQLFunctionCache record is built during the first call, * and linked to from the fn_extra field of the FmgrInfo struct. */typedef struct{	int			typlen;			/* length of the return type */	bool		typbyval;		/* true if return type is pass by value */	bool		returnsTuple;	/* true if return type is a tuple */	bool		shutdown_reg;	/* true if registered shutdown callback */	TupleTableSlot *funcSlot;	/* if one result we need to copy it before								 * we end execution of the function and								 * free stuff */	ParamListInfo paramLI;		/* Param list representing current args */	/* 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(char *src,					 Oid *argOidVect, int nargs,					 Oid rettype, bool haspolyarg);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);static void sql_exec_error_callback(void *arg);static void ShutdownSQLFunction(Datum arg);static execution_state *init_execution_state(char *src, Oid *argOidVect, int nargs,					 Oid rettype, bool haspolyarg){	execution_state *firstes;	execution_state *preves;	List	   *queryTree_list,			   *qtl_item;	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.	 */	if (haspolyarg)		check_sql_fn_retval(rettype, get_typtype(rettype), queryTree_list);	firstes = NULL;	preves = NULL;	foreach(qtl_item, queryTree_list)	{		Query	   *queryTree = lfirst(qtl_item);		Plan	   *planTree;		execution_state *newes;		planTree = pg_plan_query(queryTree);		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;	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))));	}	/* 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	 */	fcache->typlen = typeStruct->typlen;	if (typeStruct->typtype != 'c' && rettype != RECORDOID)	{		/* The return type is not a composite type, so just use byval */		fcache->typbyval = typeStruct->typbyval;		fcache->returnsTuple = false;	}	else	{		/*		 * This is a hack.	We assume here that any function returning a		 * tuple returns it by reference.  This needs to be fixed, since		 * actually the mechanism isn't quite like return-by-reference.		 */		fcache->typbyval = false;		fcache->returnsTuple = true;	}	/*	 * If we are returning exactly one result then we have to copy tuples	 * and by reference results because we have to end the execution	 * before we return the results.  When you do this everything	 * allocated by the executor (i.e. slots and tuples) is freed.	 */	if (!finfo->fn_retset && !fcache->typbyval)		fcache->funcSlot = MakeTupleTableSlot();	else		fcache->funcSlot = NULL;	/*	 * Parse and plan 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,			   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 = (Oid *) NULL;	tmp = SysCacheGetAttr(PROCOID,						  procedureTuple,						  Anum_pg_proc_prosrc,						  &isNull);	if (isNull)		elog(ERROR, "null prosrc for function %u", foid);	src = DatumGetCString(DirectFunctionCall1(textout, tmp));	fcache->func_state = init_execution_state(src, argOidVect, nargs,											  rettype, haspolyarg);	pfree(src);	ReleaseSysCache(typeTuple);	ReleaseSysCache(procedureTuple);	finfo->fn_extra = (void *) fcache;}static voidpostquel_start(execution_state *es, SQLFunctionCachePtr fcache){	Assert(es->qd == NULL);	es->qd = CreateQueryDesc(es->query, es->plan,							 None_Receiver,							 fcache->paramLI, false);	/* Utility commands don't need Executor. */	if (es->qd->operation != CMD_UTILITY)		ExecutorStart(es->qd, false, false);	es->status = F_EXEC_RUN;}static TupleTableSlot *postquel_getnext(execution_state *es){	long		count;	if (es->qd->operation == CMD_UTILITY)	{		/* Can't handle starting or committing a transaction */		if (IsA(es->qd->parsetree->utilityStmt, TransactionStmt))			ereport(ERROR,					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),					 errmsg("cannot begin/end transactions in SQL functions")));		ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest, NULL);		return (TupleTableSlot *) NULL;	}	/*	 * 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 (LAST_POSTQUEL_COMMAND(es) && es->qd->operation == CMD_SELECT)		count = 1L;	else		count = 0L;	return ExecutorRun(es->qd, ForwardScanDirection, count);}static voidpostquel_end(execution_state *es){	/* 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)		ExecutorEnd(es->qd);	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].value = fcinfo->arg[i];			paramLI[i].isnull = fcinfo->argnull[i];		}		paramLI[nargs].kind = PARAM_INVALID;	}

⌨️ 快捷键说明

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