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

📄 functions.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
			dtuptypmod = -1;		}		else		{			/* function is declared to return RECORD */			TupleDesc	tupDesc = fcache->junkFilter->jf_cleanTupType;			if (tupDesc->tdtypeid == RECORDOID &&				tupDesc->tdtypmod < 0)				assign_record_type_typmod(tupDesc);			dtuptype = tupDesc->tdtypeid;			dtuptypmod = tupDesc->tdtypmod;		}		HeapTupleHeaderSetDatumLength(dtup, t_len);		HeapTupleHeaderSetTypeId(dtup, dtuptype);		HeapTupleHeaderSetTypMod(dtup, dtuptypmod);		value = PointerGetDatum(dtup);		fcinfo->isnull = false;	}	else	{		/*		 * Returning a scalar, which we have to extract from the first column		 * of the SELECT result, and then copy into result context if needed.		 */		value = slot_getattr(slot, 1, &(fcinfo->isnull));		if (!fcinfo->isnull)			value = datumCopy(value, fcache->typbyval, fcache->typlen);	}	MemoryContextSwitchTo(oldcontext);	/*	 * If this is a single valued function we have to end the function	 * execution now.	 */	if (!fcinfo->flinfo->fn_retset)		postquel_end(es);	return value;}Datumfmgr_sql(PG_FUNCTION_ARGS){	MemoryContext oldcontext;	SQLFunctionCachePtr fcache;	ErrorContextCallback sqlerrcontext;	execution_state *es;	Datum		result = 0;	/*	 * Switch to context in which the fcache lives.  This ensures that	 * parsetrees, plans, etc, will have sufficient lifetime.  The	 * sub-executor is responsible for deleting per-tuple information.	 */	oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);	/*	 * Setup error traceback support for ereport()	 */	sqlerrcontext.callback = sql_exec_error_callback;	sqlerrcontext.arg = fcinfo->flinfo;	sqlerrcontext.previous = error_context_stack;	error_context_stack = &sqlerrcontext;	/*	 * Initialize fcache (build plans) if first time through.	 */	fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;	if (fcache == NULL)	{		init_sql_fcache(fcinfo->flinfo);		fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;	}	es = fcache->func_state;	/*	 * Convert params to appropriate format if starting a fresh execution. (If	 * continuing execution, we can re-use prior params.)	 */	if (es && es->status == F_EXEC_START)		postquel_sub_params(fcache, fcinfo);	/*	 * Find first unfinished query in function.	 */	while (es && es->status == F_EXEC_DONE)		es = es->next;	/*	 * Execute each command in the function one after another until we're	 * executing the final command and get a result or we run out of commands.	 */	while (es)	{		result = postquel_execute(es, fcinfo, fcache, oldcontext);		if (es->status != F_EXEC_DONE)			break;		es = es->next;	}	/*	 * If we've gone through every command in this function, we are done.	 */	if (es == NULL)	{		/*		 * Reset the execution states to start over again on next call.		 */		es = fcache->func_state;		while (es)		{			es->status = F_EXEC_START;			es = es->next;		}		/*		 * Let caller know we're finished.		 */		if (fcinfo->flinfo->fn_retset)		{			ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;			if (rsi && IsA(rsi, ReturnSetInfo))				rsi->isDone = ExprEndResult;			else				ereport(ERROR,						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),						 errmsg("set-valued function called in context that cannot accept a set")));			fcinfo->isnull = true;			result = (Datum) 0;			/* Deregister shutdown callback, if we made one */			if (fcache->shutdown_reg)			{				UnregisterExprContextCallback(rsi->econtext,											  ShutdownSQLFunction,											  PointerGetDatum(fcache));				fcache->shutdown_reg = false;			}		}		error_context_stack = sqlerrcontext.previous;		MemoryContextSwitchTo(oldcontext);		return result;	}	/*	 * If we got a result 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));	/*	 * Let caller know we're not finished.	 */	if (fcinfo->flinfo->fn_retset)	{		ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;		if (rsi && IsA(rsi, ReturnSetInfo))			rsi->isDone = ExprMultipleResult;		else			ereport(ERROR,					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),					 errmsg("set-valued function called in context that cannot accept a set")));		/*		 * Ensure we will get shut down cleanly if the exprcontext is not run		 * to completion.		 */		if (!fcache->shutdown_reg)		{			RegisterExprContextCallback(rsi->econtext,										ShutdownSQLFunction,										PointerGetDatum(fcache));			fcache->shutdown_reg = true;		}	}	error_context_stack = sqlerrcontext.previous;	MemoryContextSwitchTo(oldcontext);	return result;}/* * error context callback to let us supply a call-stack traceback */static voidsql_exec_error_callback(void *arg){	FmgrInfo   *flinfo = (FmgrInfo *) arg;	SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) flinfo->fn_extra;	HeapTuple	func_tuple;	Form_pg_proc functup;	char	   *fn_name;	int			syntaxerrposition;	/* Need access to function's pg_proc tuple */	func_tuple = SearchSysCache(PROCOID,								ObjectIdGetDatum(flinfo->fn_oid),								0, 0, 0);	if (!HeapTupleIsValid(func_tuple))		return;					/* shouldn't happen */	functup = (Form_pg_proc) GETSTRUCT(func_tuple);	fn_name = NameStr(functup->proname);	/*	 * If there is a syntax error position, convert to internal syntax error	 */	syntaxerrposition = geterrposition();	if (syntaxerrposition > 0)	{		bool		isnull;		Datum		tmp;		char	   *prosrc;		tmp = SysCacheGetAttr(PROCOID, func_tuple, Anum_pg_proc_prosrc,							  &isnull);		if (isnull)			elog(ERROR, "null prosrc");		prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));		errposition(0);		internalerrposition(syntaxerrposition);		internalerrquery(prosrc);		pfree(prosrc);	}	/*	 * Try to determine where in the function we failed.  If there is a query	 * with non-null QueryDesc, finger it.	(We check this rather than looking	 * for F_EXEC_RUN state, so that errors during ExecutorStart or	 * ExecutorEnd are blamed on the appropriate query; see postquel_start and	 * postquel_end.)	 */	if (fcache)	{		execution_state *es;		int			query_num;		es = fcache->func_state;		query_num = 1;		while (es)		{			if (es->qd)			{				errcontext("SQL function \"%s\" statement %d",						   fn_name, query_num);				break;			}			es = es->next;			query_num++;		}		if (es == NULL)		{			/*			 * couldn't identify a running query; might be function entry,			 * function exit, or between queries.			 */			errcontext("SQL function \"%s\"", fn_name);		}	}	else	{		/* must have failed during init_sql_fcache() */		errcontext("SQL function \"%s\" during startup", fn_name);	}	ReleaseSysCache(func_tuple);}/* * callback function in case a function-returning-set needs to be shut down * before it has been run to completion */static voidShutdownSQLFunction(Datum arg){	SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) DatumGetPointer(arg);	execution_state *es = fcache->func_state;	while (es != NULL)	{		/* Shut down anything still running */		if (es->status == F_EXEC_RUN)			postquel_end(es);		/* Reset states to START in case we're called again */		es->status = F_EXEC_START;		es = es->next;	}	/* execUtils will deregister the callback... */	fcache->shutdown_reg = false;}/* * check_sql_fn_retval() -- check return value of a list of sql parse trees. * * The return value of a sql function is the value returned by * the final query in the function.  We do some ad-hoc type checking here * to be sure that the user is returning the type he claims. * * This is normally applied during function definition, but in the case * of a function with polymorphic arguments, we instead apply it during * function execution startup.	The rettype is then the actual resolved * output type of the function, rather than the declared type.	(Therefore, * we should never see ANYARRAY or ANYELEMENT as rettype.) * * The return value is true if the function returns the entire tuple result * of its final SELECT, and false otherwise.  Note that because we allow * "SELECT rowtype_expression", this may be false even when the declared * function return type is a rowtype. * * If junkFilter isn't NULL, then *junkFilter is set to a JunkFilter defined * to convert the function's tuple result to the correct output tuple type. * Whenever the result value is false (ie, the function isn't returning a * tuple result), *junkFilter is set to NULL. */boolcheck_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,					JunkFilter **junkFilter){	Query	   *parse;	int			cmd;	List	   *tlist;	ListCell   *tlistitem;	int			tlistlen;	char		fn_typtype;	Oid			restype;	if (junkFilter)		*junkFilter = NULL;		/* default result */	/* guard against empty function body; OK only if void return type */	if (queryTreeList == NIL)	{		if (rettype != VOIDOID)			ereport(ERROR,					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),			 errmsg("return type mismatch in function declared to return %s",					format_type_be(rettype)),				 errdetail("Function's final statement must be a SELECT.")));		return false;	}	/* find the final query */	parse = (Query *) lfirst(list_tail(queryTreeList));	cmd = parse->commandType;	tlist = parse->targetList;	/*	 * The last query must be a SELECT if and only if return type isn't VOID.	 */	if (rettype == VOIDOID)	{		if (cmd == CMD_SELECT)			ereport(ERROR,					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),			 errmsg("return type mismatch in function declared to return %s",					format_type_be(rettype)),			 errdetail("Function's final statement must not be a SELECT.")));		return false;	}	/* by here, the function is declared to return some type */	if (cmd != CMD_SELECT)		ereport(ERROR,				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),			 errmsg("return type mismatch in function declared to return %s",					format_type_be(rettype)),				 errdetail("Function's final statement must be a SELECT.")));	/*	 * Count the non-junk entries in the result targetlist.	 */	tlistlen = ExecCleanTargetListLength(tlist);	fn_typtype = get_typtype(rettype);	if (fn_typtype == 'b' || fn_typtype == 'd')	{		/*		 * For base-type returns, the target list should have exactly one		 * entry, and its type should agree with what the user declared. (As		 * of Postgres 7.2, we accept binary-compatible types too.)		 */		if (tlistlen != 1)			ereport(ERROR,					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),			 errmsg("return type mismatch in function declared to return %s",					format_type_be(rettype)),				 errdetail("Final SELECT must return exactly one column.")));		restype = exprType((Node *) ((TargetEntry *) linitial(tlist))->expr);		if (!IsBinaryCoercible(restype, rettype))			ereport(ERROR,					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),			 errmsg("return type mismatch in function declared to return %s",					format_type_be(rettype)),					 errdetail("Actual return type is %s.",							   format_type_be(restype))));	}	else if (fn_typtype == 'c' || rettype == RECORDOID)	{		/* Returns a rowtype */		TupleDesc	tupdesc;		int			tupnatts;	/* physical number of columns in tuple */		int			tuplogcols; /* # of nondeleted columns in tuple */		int			colindex;	/* physical column index */		/*		 * If the target list is of length 1, and the type of the varnode in		 * the target list matches the declared return type, this is okay.		 * This can happen, for example, where the body of the function is		 * 'SELECT func2()', where func2 has the same return type as the		 * function that's calling it.		 */		if (tlistlen == 1)		{			restype = exprType((Node *) ((TargetEntry *) linitial(tlist))->expr);			if (IsBinaryCoercible(restype, rettype))				return false;	/* NOT returning whole tuple */		}		/* Is the rowtype fixed, or determined only at runtime? */		if (get_func_result_type(func_id, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)		{			/*			 * Assume we are returning the whole tuple. Crosschecking against			 * what the caller expects will happen at runtime.			 */			if (junkFilter)				*junkFilter = ExecInitJunkFilter(tlist, false, NULL);			return true;		}		Assert(tupdesc);		/*		 * Verify that the targetlist matches the return tuple type. We scan		 * the non-deleted attributes to ensure that they match the datatypes		 * of the non-resjunk columns.		 */		tupnatts = tupdesc->natts;		tuplogcols = 0;			/* we'll count nondeleted cols as we go */		colindex = 0;		foreach(tlistitem, tlist)		{			TargetEntry *tle = (TargetEntry *) lfirst(tlistitem);			Form_pg_attribute attr;			Oid			tletype;			Oid			atttype;			if (tle->resjunk)				continue;			do			{				colindex++;				if (colindex > tupnatts)					ereport(ERROR,							(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),							 errmsg("return type mismatch in function declared to return %s",									format_type_be(rettype)),					   errdetail("Final SELECT returns too many columns.")));				attr = tupdesc->attrs[colindex - 1];			} while (attr->attisdropped);			tuplogcols++;			tletype = exprType((Node *) tle->expr);			atttype = attr->atttypid;			if (!IsBinaryCoercible(tletype, atttype))				ereport(ERROR,						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),						 errmsg("return type mismatch in function declared to return %s",								format_type_be(rettype)),						 errdetail("Final SELECT returns %s instead of %s at column %d.",								   format_type_be(tletype),								   format_type_be(atttype),								   tuplogcols)));		}		for (;;)		{			colindex++;			if (colindex > tupnatts)				break;			if (!tupdesc->attrs[colindex - 1]->attisdropped)				tuplogcols++;		}		if (tlistlen != tuplogcols)			ereport(ERROR,					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),			 errmsg("return type mismatch in function declared to return %s",					format_type_be(rettype)),					 errdetail("Final SELECT returns too few columns.")));		/* Set up junk filter if needed */		if (junkFilter)			*junkFilter = ExecInitJunkFilterConversion(tlist,												CreateTupleDescCopy(tupdesc),													   NULL);		/* Report that we are returning entire tuple result */		return true;	}	else if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)	{		/* This should already have been caught ... */		ereport(ERROR,				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),				 errmsg("cannot determine result data type"),				 errdetail("A function returning \"anyarray\" or \"anyelement\" must have at least one argument of either type.")));	}	else		ereport(ERROR,				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),				 errmsg("return type %s is not supported for SQL functions",						format_type_be(rettype))));	return false;}

⌨️ 快捷键说明

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