pg_proc.c

来自「PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统」· C语言 代码 · 共 814 行 · 第 1/2 页

C
814
字号
/*------------------------------------------------------------------------- * * pg_proc.c *	  routines to support manipulation of the pg_proc relation * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.135.2.1 2005/11/22 18:23:06 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/dependency.h"#include "catalog/indexing.h"#include "catalog/pg_language.h"#include "catalog/pg_namespace.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"#include "executor/functions.h"#include "funcapi.h"#include "miscadmin.h"#include "mb/pg_wchar.h"#include "parser/parse_type.h"#include "tcop/pquery.h"#include "tcop/tcopprot.h"#include "utils/acl.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/syscache.h"/* GUC parameter */bool		check_function_bodies = true;Datum		fmgr_internal_validator(PG_FUNCTION_ARGS);Datum		fmgr_c_validator(PG_FUNCTION_ARGS);Datum		fmgr_sql_validator(PG_FUNCTION_ARGS);static void sql_function_parse_error_callback(void *arg);static int match_prosrc_to_query(const char *prosrc, const char *queryText,					  int cursorpos);static bool match_prosrc_to_literal(const char *prosrc, const char *literal,						int cursorpos, int *newcursorpos);/* ---------------------------------------------------------------- *		ProcedureCreate * * Note: allParameterTypes, parameterModes, parameterNames are either arrays * of the proper types or NULL.  We declare them Datum, not "ArrayType *", * to avoid importing array.h into pg_proc.h. * ---------------------------------------------------------------- */OidProcedureCreate(const char *procedureName,				Oid procNamespace,				bool replace,				bool returnsSet,				Oid returnType,				Oid languageObjectId,				Oid languageValidator,				const char *prosrc,				const char *probin,				bool isAgg,				bool security_definer,				bool isStrict,				char volatility,				oidvector *parameterTypes,				Datum allParameterTypes,				Datum parameterModes,				Datum parameterNames){	Oid			retval;	int			parameterCount;	int			allParamCount;	Oid		   *allParams;	bool		genericInParam = false;	bool		genericOutParam = false;	bool		internalInParam = false;	bool		internalOutParam = false;	Relation	rel;	HeapTuple	tup;	HeapTuple	oldtup;	char		nulls[Natts_pg_proc];	Datum		values[Natts_pg_proc];	char		replaces[Natts_pg_proc];	Oid			relid;	NameData	procname;	TupleDesc	tupDesc;	bool		is_update;	ObjectAddress myself,				referenced;	int			i;	/*	 * sanity checks	 */	Assert(PointerIsValid(prosrc));	Assert(PointerIsValid(probin));	parameterCount = parameterTypes->dim1;	if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS)		ereport(ERROR,				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),				 errmsg("functions cannot have more than %d arguments",						FUNC_MAX_ARGS)));	/* note: the above is correct, we do NOT count output arguments */	if (allParameterTypes != PointerGetDatum(NULL))	{		/*		 * We expect the array to be a 1-D OID array; verify that. We don't		 * need to use deconstruct_array() since the array data is just going		 * to look like a C array of OID values.		 */		allParamCount = ARR_DIMS(DatumGetPointer(allParameterTypes))[0];		if (ARR_NDIM(DatumGetPointer(allParameterTypes)) != 1 ||			allParamCount <= 0 ||			ARR_ELEMTYPE(DatumGetPointer(allParameterTypes)) != OIDOID)			elog(ERROR, "allParameterTypes is not a 1-D Oid array");		allParams = (Oid *) ARR_DATA_PTR(DatumGetPointer(allParameterTypes));		Assert(allParamCount >= parameterCount);		/* we assume caller got the contents right */	}	else	{		allParamCount = parameterCount;		allParams = parameterTypes->values;	}	/*	 * Do not allow return type ANYARRAY or ANYELEMENT unless at least one	 * input argument is ANYARRAY or ANYELEMENT.  Also, do not allow return	 * type INTERNAL unless at least one input argument is INTERNAL.	 */	for (i = 0; i < parameterCount; i++)	{		switch (parameterTypes->values[i])		{			case ANYARRAYOID:			case ANYELEMENTOID:				genericInParam = true;				break;			case INTERNALOID:				internalInParam = true;				break;		}	}	if (allParameterTypes != PointerGetDatum(NULL))	{		for (i = 0; i < allParamCount; i++)		{			/*			 * We don't bother to distinguish input and output params here, so			 * if there is, say, just an input INTERNAL param then we will			 * still set internalOutParam.	This is OK since we don't really			 * care.			 */			switch (allParams[i])			{				case ANYARRAYOID:				case ANYELEMENTOID:					genericOutParam = true;					break;				case INTERNALOID:					internalOutParam = true;					break;			}		}	}	if ((returnType == ANYARRAYOID || returnType == ANYELEMENTOID ||		 genericOutParam) && !genericInParam)		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.")));	if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)		ereport(ERROR,				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),				 errmsg("unsafe use of pseudo-type \"internal\""),				 errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));	/*	 * don't allow functions of complex types that have the same name as	 * existing attributes of the type	 */	if (parameterCount == 1 &&		OidIsValid(parameterTypes->values[0]) &&		(relid = typeidTypeRelid(parameterTypes->values[0])) != InvalidOid &&		get_attnum(relid, procedureName) != InvalidAttrNumber)		ereport(ERROR,				(errcode(ERRCODE_DUPLICATE_COLUMN),				 errmsg("\"%s\" is already an attribute of type %s",						procedureName,						format_type_be(parameterTypes->values[0]))));	/*	 * All seems OK; prepare the data to be inserted into pg_proc.	 */	for (i = 0; i < Natts_pg_proc; ++i)	{		nulls[i] = ' ';		values[i] = (Datum) 0;		replaces[i] = 'r';	}	namestrcpy(&procname, procedureName);	values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname);	values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace);	values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(GetUserId());	values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId);	values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg);	values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);	values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);	values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet);	values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility);	values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount);	values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType);	values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes);	if (allParameterTypes != PointerGetDatum(NULL))		values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes;	else		nulls[Anum_pg_proc_proallargtypes - 1] = 'n';	if (parameterModes != PointerGetDatum(NULL))		values[Anum_pg_proc_proargmodes - 1] = parameterModes;	else		nulls[Anum_pg_proc_proargmodes - 1] = 'n';	if (parameterNames != PointerGetDatum(NULL))		values[Anum_pg_proc_proargnames - 1] = parameterNames;	else		nulls[Anum_pg_proc_proargnames - 1] = 'n';	values[Anum_pg_proc_prosrc - 1] = DirectFunctionCall1(textin,													CStringGetDatum(prosrc));	values[Anum_pg_proc_probin - 1] = DirectFunctionCall1(textin,													CStringGetDatum(probin));	/* start out with empty permissions */	nulls[Anum_pg_proc_proacl - 1] = 'n';	rel = heap_open(ProcedureRelationId, RowExclusiveLock);	tupDesc = RelationGetDescr(rel);	/* Check for pre-existing definition */	oldtup = SearchSysCache(PROCNAMEARGSNSP,							PointerGetDatum(procedureName),							PointerGetDatum(parameterTypes),							ObjectIdGetDatum(procNamespace),							0);	if (HeapTupleIsValid(oldtup))	{		/* There is one; okay to replace it? */		Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);		if (!replace)			ereport(ERROR,					(errcode(ERRCODE_DUPLICATE_FUNCTION),			errmsg("function \"%s\" already exists with same argument types",				   procedureName)));		if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), GetUserId()))			aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,						   procedureName);		/*		 * Not okay to change the return type of the existing proc, since		 * existing rules, views, etc may depend on the return type.		 */		if (returnType != oldproc->prorettype ||			returnsSet != oldproc->proretset)			ereport(ERROR,					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),					 errmsg("cannot change return type of existing function"),					 errhint("Use DROP FUNCTION first.")));		/*		 * If it returns RECORD, check for possible change of record type		 * implied by OUT parameters		 */		if (returnType == RECORDOID)		{			TupleDesc	olddesc;			TupleDesc	newdesc;			olddesc = build_function_result_tupdesc_t(oldtup);			newdesc = build_function_result_tupdesc_d(allParameterTypes,													  parameterModes,													  parameterNames);			if (olddesc == NULL && newdesc == NULL)				 /* ok, both are runtime-defined RECORDs */ ;			else if (olddesc == NULL || newdesc == NULL ||					 !equalTupleDescs(olddesc, newdesc))				ereport(ERROR,						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),					errmsg("cannot change return type of existing function"),				errdetail("Row type defined by OUT parameters is different."),						 errhint("Use DROP FUNCTION first.")));		}		/* Can't change aggregate status, either */		if (oldproc->proisagg != isAgg)		{			if (oldproc->proisagg)				ereport(ERROR,						(errcode(ERRCODE_WRONG_OBJECT_TYPE),						 errmsg("function \"%s\" is an aggregate",								procedureName)));			else				ereport(ERROR,						(errcode(ERRCODE_WRONG_OBJECT_TYPE),						 errmsg("function \"%s\" is not an aggregate",								procedureName)));		}		/* do not change existing ownership or permissions, either */		replaces[Anum_pg_proc_proowner - 1] = ' ';		replaces[Anum_pg_proc_proacl - 1] = ' ';		/* Okay, do it... */		tup = heap_modifytuple(oldtup, tupDesc, values, nulls, replaces);		simple_heap_update(rel, &tup->t_self, tup);		ReleaseSysCache(oldtup);		is_update = true;	}	else	{		/* Creating a new procedure */		tup = heap_formtuple(tupDesc, values, nulls);		simple_heap_insert(rel, tup);		is_update = false;	}	/* Need to update indexes for either the insert or update case */	CatalogUpdateIndexes(rel, tup);	retval = HeapTupleGetOid(tup);	/*	 * Create dependencies for the new function.  If we are updating an	 * existing function, first delete any existing pg_depend entries.	 */	if (is_update)	{		deleteDependencyRecordsFor(ProcedureRelationId, retval);		deleteSharedDependencyRecordsFor(ProcedureRelationId, retval);	}	myself.classId = ProcedureRelationId;	myself.objectId = retval;	myself.objectSubId = 0;	/* dependency on namespace */	referenced.classId = NamespaceRelationId;	referenced.objectId = procNamespace;	referenced.objectSubId = 0;	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);	/* dependency on implementation language */	referenced.classId = LanguageRelationId;	referenced.objectId = languageObjectId;	referenced.objectSubId = 0;	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);	/* dependency on return type */	referenced.classId = TypeRelationId;	referenced.objectId = returnType;	referenced.objectSubId = 0;	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);	/* dependency on parameter types */	for (i = 0; i < allParamCount; i++)	{		referenced.classId = TypeRelationId;		referenced.objectId = allParams[i];		referenced.objectSubId = 0;		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);	}	/* dependency on owner */	recordDependencyOnOwner(ProcedureRelationId, retval, GetUserId());	heap_freetuple(tup);	heap_close(rel, RowExclusiveLock);	/* Verify function body */	if (OidIsValid(languageValidator))	{		/* Advance command counter so new tuple can be seen by validator */		CommandCounterIncrement();		OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval));	}	return retval;}

⌨️ 快捷键说明

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