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

📄 funcapi.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
/*------------------------------------------------------------------------- * * funcapi.c *	  Utility and convenience functions for fmgr functions that return *	  sets and/or composite types. * * Copyright (c) 2002-2005, PostgreSQL Global Development Group * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.26.2.1 2006/01/17 17:33:21 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "funcapi.h"#include "catalog/namespace.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"#include "parser/parse_coerce.h"#include "parser/parse_expr.h"#include "utils/array.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/syscache.h"#include "utils/typcache.h"static void shutdown_MultiFuncCall(Datum arg);static TypeFuncClass internal_get_result_type(Oid funcid,						 Node *call_expr,						 ReturnSetInfo *rsinfo,						 Oid *resultTypeId,						 TupleDesc *resultTupleDesc);static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,							oidvector *declared_args,							Node *call_expr);static TypeFuncClass get_type_func_class(Oid typid);/* * init_MultiFuncCall * Create an empty FuncCallContext data structure * and do some other basic Multi-function call setup * and error checking */FuncCallContext *init_MultiFuncCall(PG_FUNCTION_ARGS){	FuncCallContext *retval;	/*	 * Bail if we're called in the wrong context	 */	if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))		ereport(ERROR,				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),				 errmsg("set-valued function called in context that cannot accept a set")));	if (fcinfo->flinfo->fn_extra == NULL)	{		/*		 * First call		 */		ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;		/*		 * Allocate suitably long-lived space and zero it		 */		retval = (FuncCallContext *)			MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,								   sizeof(FuncCallContext));		/*		 * initialize the elements		 */		retval->call_cntr = 0;		retval->max_calls = 0;		retval->slot = NULL;		retval->user_fctx = NULL;		retval->attinmeta = NULL;		retval->tuple_desc = NULL;		retval->multi_call_memory_ctx = fcinfo->flinfo->fn_mcxt;		/*		 * save the pointer for cross-call use		 */		fcinfo->flinfo->fn_extra = retval;		/*		 * Ensure we will get shut down cleanly if the exprcontext is not run		 * to completion.		 */		RegisterExprContextCallback(rsi->econtext,									shutdown_MultiFuncCall,									PointerGetDatum(fcinfo->flinfo));	}	else	{		/* second and subsequent calls */		elog(ERROR, "init_MultiFuncCall may not be called more than once");		/* never reached, but keep compiler happy */		retval = NULL;	}	return retval;}/* * per_MultiFuncCall * * Do Multi-function per-call setup */FuncCallContext *per_MultiFuncCall(PG_FUNCTION_ARGS){	FuncCallContext *retval = (FuncCallContext *) fcinfo->flinfo->fn_extra;	/*	 * Clear the TupleTableSlot, if present.  This is for safety's sake: the	 * Slot will be in a long-lived context (it better be, if the	 * FuncCallContext is pointing to it), but in most usage patterns the	 * tuples stored in it will be in the function's per-tuple context. So at	 * the beginning of each call, the Slot will hold a dangling pointer to an	 * already-recycled tuple.	We clear it out here.	 *	 * Note: use of retval->slot is obsolete as of 8.0, and we expect that it	 * will always be NULL.  This is just here for backwards compatibility in	 * case someone creates a slot anyway.	 */	if (retval->slot != NULL)		ExecClearTuple(retval->slot);	return retval;}/* * end_MultiFuncCall * Clean up after init_MultiFuncCall */voidend_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx){	ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;	/* Deregister the shutdown callback */	UnregisterExprContextCallback(rsi->econtext,								  shutdown_MultiFuncCall,								  PointerGetDatum(fcinfo->flinfo));	/* But use it to do the real work */	shutdown_MultiFuncCall(PointerGetDatum(fcinfo->flinfo));}/* * shutdown_MultiFuncCall * Shutdown function to clean up after init_MultiFuncCall */static voidshutdown_MultiFuncCall(Datum arg){	FmgrInfo   *flinfo = (FmgrInfo *) DatumGetPointer(arg);	FuncCallContext *funcctx = (FuncCallContext *) flinfo->fn_extra;	/* unbind from flinfo */	flinfo->fn_extra = NULL;	/*	 * Caller is responsible to free up memory for individual struct elements	 * other than att_in_funcinfo and elements.	 */	if (funcctx->attinmeta != NULL)		pfree(funcctx->attinmeta);	pfree(funcctx);}/* * get_call_result_type *		Given a function's call info record, determine the kind of datatype *		it is supposed to return.  If resultTypeId isn't NULL, *resultTypeId *		receives the actual datatype OID (this is mainly useful for scalar *		result types).	If resultTupleDesc isn't NULL, *resultTupleDesc *		receives a pointer to a TupleDesc when the result is of a composite *		type, or NULL when it's a scalar result.  NB: the tupledesc should *		be copied if it is to be accessed over a long period. * * One hard case that this handles is resolution of actual rowtypes for * functions returning RECORD (from either the function's OUT parameter * list, or a ReturnSetInfo context node).	TYPEFUNC_RECORD is returned * only when we couldn't resolve the actual rowtype for lack of information. * * The other hard case that this handles is resolution of polymorphism. * We will never return ANYELEMENT or ANYARRAY, either as a scalar result * type or as a component of a rowtype. * * This function is relatively expensive --- in a function returning set, * try to call it only the first time through. */TypeFuncClassget_call_result_type(FunctionCallInfo fcinfo,					 Oid *resultTypeId,					 TupleDesc *resultTupleDesc){	return internal_get_result_type(fcinfo->flinfo->fn_oid,									fcinfo->flinfo->fn_expr,									(ReturnSetInfo *) fcinfo->resultinfo,									resultTypeId,									resultTupleDesc);}/* * get_expr_result_type *		As above, but work from a calling expression node tree */TypeFuncClassget_expr_result_type(Node *expr,					 Oid *resultTypeId,					 TupleDesc *resultTupleDesc){	TypeFuncClass result;	if (expr && IsA(expr, FuncExpr))		result = internal_get_result_type(((FuncExpr *) expr)->funcid,										  expr,										  NULL,										  resultTypeId,										  resultTupleDesc);	else if (expr && IsA(expr, OpExpr))		result = internal_get_result_type(get_opcode(((OpExpr *) expr)->opno),										  expr,										  NULL,										  resultTypeId,										  resultTupleDesc);	else	{		/* handle as a generic expression; no chance to resolve RECORD */		Oid			typid = exprType(expr);		if (resultTypeId)			*resultTypeId = typid;		if (resultTupleDesc)			*resultTupleDesc = NULL;		result = get_type_func_class(typid);		if (result == TYPEFUNC_COMPOSITE && resultTupleDesc)			*resultTupleDesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(typid, -1));	}	return result;}/* * get_func_result_type *		As above, but work from a function's OID only * * This will not be able to resolve pure-RECORD results nor polymorphism. */TypeFuncClassget_func_result_type(Oid functionId,					 Oid *resultTypeId,					 TupleDesc *resultTupleDesc){	return internal_get_result_type(functionId,									NULL,									NULL,									resultTypeId,									resultTupleDesc);}/* * internal_get_result_type -- workhorse code implementing all the above * * funcid must always be supplied.	call_expr and rsinfo can be NULL if not * available.  We will return TYPEFUNC_RECORD, and store NULL into * *resultTupleDesc, if we cannot deduce the complete result rowtype from * the available information. */static TypeFuncClassinternal_get_result_type(Oid funcid,						 Node *call_expr,						 ReturnSetInfo *rsinfo,						 Oid *resultTypeId,						 TupleDesc *resultTupleDesc){	TypeFuncClass result;	HeapTuple	tp;	Form_pg_proc procform;	Oid			rettype;	TupleDesc	tupdesc;	/* First fetch the function's pg_proc row to inspect its rettype */	tp = SearchSysCache(PROCOID,						ObjectIdGetDatum(funcid),						0, 0, 0);	if (!HeapTupleIsValid(tp))		elog(ERROR, "cache lookup failed for function %u", funcid);	procform = (Form_pg_proc) GETSTRUCT(tp);	rettype = procform->prorettype;	/* Check for OUT parameters defining a RECORD result */	tupdesc = build_function_result_tupdesc_t(tp);	if (tupdesc)	{		/*		 * It has OUT parameters, so it's basically like a regular composite		 * type, except we have to be able to resolve any polymorphic OUT		 * parameters.		 */		if (resultTypeId)			*resultTypeId = rettype;		if (resolve_polymorphic_tupdesc(tupdesc,										&procform->proargtypes,										call_expr))		{			if (tupdesc->tdtypeid == RECORDOID &&				tupdesc->tdtypmod < 0)				assign_record_type_typmod(tupdesc);			if (resultTupleDesc)				*resultTupleDesc = tupdesc;			result = TYPEFUNC_COMPOSITE;		}		else		{			if (resultTupleDesc)				*resultTupleDesc = NULL;			result = TYPEFUNC_RECORD;		}		ReleaseSysCache(tp);		return result;	}	/*	 * If scalar polymorphic result, try to resolve it.	 */	if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)	{		Oid			newrettype = exprType(call_expr);		if (newrettype == InvalidOid)	/* this probably should not happen */			ereport(ERROR,					(errcode(ERRCODE_DATATYPE_MISMATCH),					 errmsg("could not determine actual result type for function \"%s\" declared to return type %s",							NameStr(procform->proname),							format_type_be(rettype))));		rettype = newrettype;	}	if (resultTypeId)		*resultTypeId = rettype;	if (resultTupleDesc)		*resultTupleDesc = NULL;	/* default result */	/* Classify the result type */	result = get_type_func_class(rettype);	switch (result)	{		case TYPEFUNC_COMPOSITE:			if (resultTupleDesc)				*resultTupleDesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(rettype, -1));			/* Named composite types can't have any polymorphic columns */			break;		case TYPEFUNC_SCALAR:			break;		case TYPEFUNC_RECORD:			/* We must get the tupledesc from call context */			if (rsinfo && IsA(rsinfo, ReturnSetInfo) &&				rsinfo->expectedDesc != NULL)			{				result = TYPEFUNC_COMPOSITE;				if (resultTupleDesc)					*resultTupleDesc = rsinfo->expectedDesc;				/* Assume no polymorphic columns here, either */			}			break;		default:			break;	}	ReleaseSysCache(tp);	return result;}/* * Given the result tuple descriptor for a function with OUT parameters, * replace any polymorphic columns (ANYELEMENT/ANYARRAY) with correct data * types deduced from the input arguments.	Returns TRUE if able to deduce * all types, FALSE if not. */static boolresolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,							Node *call_expr){	int			natts = tupdesc->natts;	int			nargs = declared_args->dim1;	bool		have_anyelement_result = false;	bool		have_anyarray_result = false;	Oid			anyelement_type = InvalidOid;	Oid			anyarray_type = InvalidOid;	int			i;	/* See if there are any polymorphic outputs; quick out if not */	for (i = 0; i < natts; i++)	{		switch (tupdesc->attrs[i]->atttypid)		{			case ANYELEMENTOID:				have_anyelement_result = true;				break;			case ANYARRAYOID:				have_anyarray_result = true;				break;			default:				break;		}	}	if (!have_anyelement_result && !have_anyarray_result)		return true;	/*	 * Otherwise, extract actual datatype(s) from input arguments.	(We assume	 * the parser already validated consistency of the arguments.)	 */	if (!call_expr)		return false;			/* no hope */	for (i = 0; i < nargs; i++)	{		switch (declared_args->values[i])		{			case ANYELEMENTOID:				if (!OidIsValid(anyelement_type))					anyelement_type = get_call_expr_argtype(call_expr, i);				break;			case ANYARRAYOID:				if (!OidIsValid(anyarray_type))					anyarray_type = get_call_expr_argtype(call_expr, i);				break;			default:				break;		}	}	/* If nothing found, parser messed up */	if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))		return false;	/* If needed, deduce one polymorphic type from the other */	if (have_anyelement_result && !OidIsValid(anyelement_type))		anyelement_type = resolve_generic_type(ANYELEMENTOID,											   anyarray_type,											   ANYARRAYOID);	if (have_anyarray_result && !OidIsValid(anyarray_type))		anyarray_type = resolve_generic_type(ANYARRAYOID,											 anyelement_type,											 ANYELEMENTOID);	/* And finally replace the tuple column types as needed */	for (i = 0; i < natts; i++)	{		switch (tupdesc->attrs[i]->atttypid)		{			case ANYELEMENTOID:				TupleDescInitEntry(tupdesc, i + 1,								   NameStr(tupdesc->attrs[i]->attname),								   anyelement_type,								   -1,								   0);				break;			case ANYARRAYOID:				TupleDescInitEntry(tupdesc, i + 1,								   NameStr(tupdesc->attrs[i]->attname),								   anyarray_type,								   -1,								   0);				break;			default:				break;		}	}	return true;}/* * Given the declared argument types and modes for a function, * replace any polymorphic types (ANYELEMENT/ANYARRAY) with correct data * types deduced from the input arguments.	Returns TRUE if able to deduce * all types, FALSE if not.  This is the same logic as * resolve_polymorphic_tupdesc, but with a different argument representation. * * argmodes may be NULL, in which case all arguments are assumed to be IN mode. */boolresolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,							 Node *call_expr){	bool		have_anyelement_result = false;	bool		have_anyarray_result = false;	Oid			anyelement_type = InvalidOid;	Oid			anyarray_type = InvalidOid;	int			inargno;

⌨️ 快捷键说明

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