fmgr.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,673 行 · 第 1/3 页

C
1,673
字号
/*------------------------------------------------------------------------- * * fmgr.c *	  The Postgres function manager. * * 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/utils/fmgr/fmgr.c,v 1.76 2003/09/25 06:58:05 petere Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/tuptoaster.h"#include "catalog/pg_language.h"#include "catalog/pg_proc.h"#include "executor/functions.h"#include "miscadmin.h"#include "parser/parse_expr.h"#include "utils/builtins.h"#include "utils/fmgrtab.h"#include "utils/lsyscache.h"#include "utils/syscache.h"/* * Declaration for old-style function pointer type.  This is now used only * in fmgr_oldstyle() and is no longer exported. * * The m68k SVR4 ABI defines that pointers are returned in %a0 instead of * %d0. So if a function pointer is declared to return a pointer, the * compiler may look only into %a0, but if the called function was declared * to return an integer type, it puts its value only into %d0. So the * caller doesn't pink up the correct return value. The solution is to * declare the function pointer to return int, so the compiler picks up the * return value from %d0. (Functions returning pointers put their value * *additionally* into %d0 for compatibility.) The price is that there are * some warnings about int->pointer conversions... */#if defined(__mc68000__) && defined(__ELF__)typedef int32 ((*func_ptr) ());#elsetypedef char *((*func_ptr) ());#endif/* * For an oldstyle function, fn_extra points to a record like this: */typedef struct{	func_ptr	func;			/* Address of the oldstyle function */	bool		arg_toastable[FUNC_MAX_ARGS];	/* is n'th arg of a												 * toastable datatype? */} Oldstyle_fnextra;static void fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,					   bool ignore_security);static void fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple);static void fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple);static Datum fmgr_oldstyle(PG_FUNCTION_ARGS);static Datum fmgr_security_definer(PG_FUNCTION_ARGS);/* * Lookup routines for builtin-function table.	We can search by either Oid * or name, but search by Oid is much faster. */static const FmgrBuiltin *fmgr_isbuiltin(Oid id){	int			low = 0;	int			high = fmgr_nbuiltins - 1;	/*	 * Loop invariant: low is the first index that could contain target	 * entry, and high is the last index that could contain it.	 */	while (low <= high)	{		int			i = (high + low) / 2;		const FmgrBuiltin *ptr = &fmgr_builtins[i];		if (id == ptr->foid)			return ptr;		else if (id > ptr->foid)			low = i + 1;		else			high = i - 1;	}	return (const FmgrBuiltin *) NULL;}/* * Lookup a builtin by name.  Note there can be more than one entry in * the array with the same name, but they should all point to the same * routine. */static const FmgrBuiltin *fmgr_lookupByName(const char *name){	int			i;	for (i = 0; i < fmgr_nbuiltins; i++)	{		if (strcmp(name, fmgr_builtins[i].funcName) == 0)			return fmgr_builtins + i;	}	return (const FmgrBuiltin *) NULL;}/* * This routine fills a FmgrInfo struct, given the OID * of the function to be called. * * The caller's CurrentMemoryContext is used as the fn_mcxt of the info * struct; this means that any subsidiary data attached to the info struct * (either by fmgr_info itself, or later on by a function call handler) * will be allocated in that context.  The caller must ensure that this * context is at least as long-lived as the info struct itself.  This is * not a problem in typical cases where the info struct is on the stack or * in freshly-palloc'd space.  However, if one intends to store an info * struct in a long-lived table, it's better to use fmgr_info_cxt. */voidfmgr_info(Oid functionId, FmgrInfo *finfo){	fmgr_info_cxt(functionId, finfo, CurrentMemoryContext);}/* * Fill a FmgrInfo struct, specifying a memory context in which its * subsidiary data should go. */voidfmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt){	fmgr_info_cxt_security(functionId, finfo, mcxt, false);}/* * This one does the actual work.  ignore_security is ordinarily false * but is set to true by fmgr_security_definer to avoid infinite * recursive lookups. */static voidfmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,					   bool ignore_security){	const FmgrBuiltin *fbp;	HeapTuple	procedureTuple;	Form_pg_proc procedureStruct;	char	   *prosrc;	/*	 * fn_oid *must* be filled in last.  Some code assumes that if fn_oid	 * is valid, the whole struct is valid.  Some FmgrInfo struct's do	 * survive elogs.	 */	finfo->fn_oid = InvalidOid;	finfo->fn_extra = NULL;	finfo->fn_mcxt = mcxt;	finfo->fn_expr = NULL;		/* caller may set this later */	if ((fbp = fmgr_isbuiltin(functionId)) != NULL)	{		/*		 * Fast path for builtin functions: don't bother consulting		 * pg_proc		 */		finfo->fn_nargs = fbp->nargs;		finfo->fn_strict = fbp->strict;		finfo->fn_retset = fbp->retset;		finfo->fn_addr = fbp->func;		finfo->fn_oid = functionId;		return;	}	/* Otherwise we need the pg_proc entry */	procedureTuple = SearchSysCache(PROCOID,									ObjectIdGetDatum(functionId),									0, 0, 0);	if (!HeapTupleIsValid(procedureTuple))		elog(ERROR, "cache lookup failed for function %u", functionId);	procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);	finfo->fn_nargs = procedureStruct->pronargs;	finfo->fn_strict = procedureStruct->proisstrict;	finfo->fn_retset = procedureStruct->proretset;	if (procedureStruct->prosecdef && !ignore_security)	{		finfo->fn_addr = fmgr_security_definer;		finfo->fn_oid = functionId;		ReleaseSysCache(procedureTuple);		return;	}	switch (procedureStruct->prolang)	{		case INTERNALlanguageId:			/*			 * For an ordinary builtin function, we should never get here			 * because the isbuiltin() search above will have succeeded.			 * However, if the user has done a CREATE FUNCTION to create			 * an alias for a builtin function, we can end up here.  In			 * that case we have to look up the function by name.  The			 * name of the internal function is stored in prosrc (it			 * doesn't have to be the same as the name of the alias!)			 */			prosrc = DatumGetCString(DirectFunctionCall1(textout,							 PointerGetDatum(&procedureStruct->prosrc)));			fbp = fmgr_lookupByName(prosrc);			if (fbp == NULL)				ereport(ERROR,						(errcode(ERRCODE_UNDEFINED_FUNCTION),					   errmsg("internal function \"%s\" is not in internal lookup table",							  prosrc)));			pfree(prosrc);			/* Should we check that nargs, strict, retset match the table? */			finfo->fn_addr = fbp->func;			break;		case ClanguageId:			fmgr_info_C_lang(functionId, finfo, procedureTuple);			break;		case SQLlanguageId:			finfo->fn_addr = fmgr_sql;			break;		default:			fmgr_info_other_lang(functionId, finfo, procedureTuple);			break;	}	finfo->fn_oid = functionId;	ReleaseSysCache(procedureTuple);}/* * Special fmgr_info processing for C-language functions.  Note that * finfo->fn_oid is not valid yet. */static voidfmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple){	Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);	Datum		prosrcattr,				probinattr;	char	   *prosrcstring,			   *probinstring;	void	   *libraryhandle;	PGFunction	user_fn;	Pg_finfo_record *inforec;	Oldstyle_fnextra *fnextra;	bool		isnull;	int			i;	/* Get prosrc and probin strings (link symbol and library filename) */	prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple,								 Anum_pg_proc_prosrc, &isnull);	if (isnull)		elog(ERROR, "null prosrc for function %u", functionId);	prosrcstring = DatumGetCString(DirectFunctionCall1(textout, prosrcattr));	probinattr = SysCacheGetAttr(PROCOID, procedureTuple,								 Anum_pg_proc_probin, &isnull);	if (isnull)		elog(ERROR, "null probin for function %u", functionId);	probinstring = DatumGetCString(DirectFunctionCall1(textout, probinattr));	/* Look up the function itself */	user_fn = load_external_function(probinstring, prosrcstring, true,									 &libraryhandle);	/* Get the function information record (real or default) */	inforec = fetch_finfo_record(libraryhandle, prosrcstring);	switch (inforec->api_version)	{		case 0:			/* Old style: need to use a handler */			finfo->fn_addr = fmgr_oldstyle;			fnextra = (Oldstyle_fnextra *)				MemoryContextAlloc(finfo->fn_mcxt, sizeof(Oldstyle_fnextra));			finfo->fn_extra = (void *) fnextra;			MemSet(fnextra, 0, sizeof(Oldstyle_fnextra));			fnextra->func = (func_ptr) user_fn;			for (i = 0; i < procedureStruct->pronargs; i++)			{				fnextra->arg_toastable[i] =					TypeIsToastable(procedureStruct->proargtypes[i]);			}			break;		case 1:			/* New style: call directly */			finfo->fn_addr = user_fn;			break;		default:			/* Shouldn't get here if fetch_finfo_record did its job */			elog(ERROR, "unrecognized function API version: %d",				 inforec->api_version);			break;	}	pfree(prosrcstring);	pfree(probinstring);}/* * Special fmgr_info processing for other-language functions.  Note * that finfo->fn_oid is not valid yet. */static voidfmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple){	Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);	Oid			language = procedureStruct->prolang;	HeapTuple	languageTuple;	Form_pg_language languageStruct;	FmgrInfo	plfinfo;	languageTuple = SearchSysCache(LANGOID,								   ObjectIdGetDatum(language),								   0, 0, 0);	if (!HeapTupleIsValid(languageTuple))		elog(ERROR, "cache lookup failed for language %u", language);	languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);	fmgr_info(languageStruct->lanplcallfoid, &plfinfo);	finfo->fn_addr = plfinfo.fn_addr;	/*	 * If lookup of the PL handler function produced nonnull fn_extra,	 * complain --- it must be an oldstyle function! We no longer support	 * oldstyle PL handlers.	 */	if (plfinfo.fn_extra != NULL)		elog(ERROR, "language %u has old-style handler", language);	ReleaseSysCache(languageTuple);}/* * Fetch and validate the information record for the given external function. * The function is specified by a handle for the containing library * (obtained from load_external_function) as well as the function name. * * If no info function exists for the given name, it is not an error. * Instead we return a default info record for a version-0 function. * We want to raise an error here only if the info function returns * something bogus. * * This function is broken out of fmgr_info_C_lang() so that ProcedureCreate() * can validate the information record for a function not yet entered into * pg_proc. */Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname){	char	   *infofuncname;	PGFInfoFunction infofunc;	Pg_finfo_record *inforec;	static Pg_finfo_record default_inforec = {0};	/* Compute name of info func */	infofuncname = (char *) palloc(strlen(funcname) + 10);	strcpy(infofuncname, "pg_finfo_");	strcat(infofuncname, funcname);	/* Try to look up the info function */	infofunc = (PGFInfoFunction) lookup_external_function(filehandle,														  infofuncname);	if (infofunc == (PGFInfoFunction) NULL)	{		/* Not found --- assume version 0 */		pfree(infofuncname);		return &default_inforec;	}	/* Found, so call it */	inforec = (*infofunc) ();	/* Validate result as best we can */	if (inforec == NULL)		elog(ERROR, "null result from info function \"%s\"", infofuncname);	switch (inforec->api_version)	{		case 0:		case 1:			/* OK, no additional fields to validate */			break;		default:			ereport(ERROR,					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),					 errmsg("unrecognized API version %d reported by info function \"%s\"",							inforec->api_version, infofuncname)));			break;	}	pfree(infofuncname);	return inforec;}/* * Copy an FmgrInfo struct * * This is inherently somewhat bogus since we can't reliably duplicate * language-dependent subsidiary info.	We cheat by zeroing fn_extra, * instead, meaning that subsidiary info will have to be recomputed. */voidfmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo,			   MemoryContext destcxt){	memcpy(dstinfo, srcinfo, sizeof(FmgrInfo));	dstinfo->fn_mcxt = destcxt;	if (dstinfo->fn_addr == fmgr_oldstyle)	{		/* For oldstyle functions we must copy fn_extra */		Oldstyle_fnextra *fnextra;		fnextra = (Oldstyle_fnextra *)			MemoryContextAlloc(destcxt, sizeof(Oldstyle_fnextra));		memcpy(fnextra, srcinfo->fn_extra, sizeof(Oldstyle_fnextra));		dstinfo->fn_extra = (void *) fnextra;	}	else		dstinfo->fn_extra = NULL;}/* * Specialized lookup routine for ProcedureCreate(): given the alleged name * of an internal function, return the OID of the function. * If the name is not recognized, return InvalidOid. */Oidfmgr_internal_function(const char *proname){	const FmgrBuiltin *fbp = fmgr_lookupByName(proname);	if (fbp == NULL)		return InvalidOid;	return fbp->foid;}/* * Handler for old-style "C" language functions */static Datumfmgr_oldstyle(PG_FUNCTION_ARGS){	Oldstyle_fnextra *fnextra;	int			n_arguments = fcinfo->nargs;	int			i;	bool		isnull;	func_ptr	user_fn;	char	   *returnValue;	if (fcinfo->flinfo == NULL || fcinfo->flinfo->fn_extra == NULL)		elog(ERROR, "fmgr_oldstyle received NULL pointer");	fnextra = (Oldstyle_fnextra *) fcinfo->flinfo->fn_extra;	/*	 * Result is NULL if any argument is NULL, but we still call the	 * function (peculiar, but that's the way it worked before, and after	 * all this is a backwards-compatibility wrapper).	Note, however,	 * that we'll never get here with NULL arguments if the function is	 * marked strict.	 *	 * We also need to detoast any TOAST-ed inputs, since it's unlikely that	 * an old-style function knows about TOASTing.	 */	isnull = false;	for (i = 0; i < n_arguments; i++)	{		if (PG_ARGISNULL(i))			isnull = true;		else if (fnextra->arg_toastable[i])			fcinfo->arg[i] = PointerGetDatum(PG_DETOAST_DATUM(fcinfo->arg[i]));	}	fcinfo->isnull = isnull;	user_fn = fnextra->func;	switch (n_arguments)	{		case 0:			returnValue = (*user_fn) ();			break;		case 1:			/*			 * nullvalue() used to use isNull to check if arg is NULL;			 * perhaps there are other functions still out there that also			 * rely on this undocumented hack?			 */			returnValue = (*user_fn) (fcinfo->arg[0], &fcinfo->isnull);			break;		case 2:			returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1]);			break;		case 3:			returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1],									  fcinfo->arg[2]);			break;		case 4:			returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1],									  fcinfo->arg[2], fcinfo->arg[3]);			break;		case 5:			returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1],									  fcinfo->arg[2], fcinfo->arg[3],									  fcinfo->arg[4]);			break;		case 6:			returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1],									  fcinfo->arg[2], fcinfo->arg[3],									  fcinfo->arg[4], fcinfo->arg[5]);			break;		case 7:			returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1],									  fcinfo->arg[2], fcinfo->arg[3],									  fcinfo->arg[4], fcinfo->arg[5],									  fcinfo->arg[6]);			break;		case 8:			returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1],									  fcinfo->arg[2], fcinfo->arg[3],									  fcinfo->arg[4], fcinfo->arg[5],									  fcinfo->arg[6], fcinfo->arg[7]);			break;		case 9:			returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1],									  fcinfo->arg[2], fcinfo->arg[3],									  fcinfo->arg[4], fcinfo->arg[5],									  fcinfo->arg[6], fcinfo->arg[7],									  fcinfo->arg[8]);			break;		case 10:			returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1],									  fcinfo->arg[2], fcinfo->arg[3],									  fcinfo->arg[4], fcinfo->arg[5],									  fcinfo->arg[6], fcinfo->arg[7],									  fcinfo->arg[8], fcinfo->arg[9]);			break;		case 11:			returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1],

⌨️ 快捷键说明

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