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

📄 pl_comp.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 4 页
字号:
/********************************************************************** * pl_comp.c		- Compiler part of the PL/pgSQL *			  procedural language * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.94.2.2 2005/12/09 17:09:00 tgl Exp $ * *	  This software is copyrighted by Jan Wieck - Hamburg. * *	  The author hereby grants permission  to  use,  copy,	modify, *	  distribute,  and	license this software and its documentation *	  for any purpose, provided that existing copyright notices are *	  retained	in	all  copies  and  that	this notice is included *	  verbatim in any distributions. No written agreement, license, *	  or  royalty  fee	is required for any of the authorized uses. *	  Modifications to this software may be  copyrighted  by  their *	  author  and  need  not  follow  the licensing terms described *	  here, provided that the new terms are  clearly  indicated  on *	  the first page of each file where they apply. * *	  IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY *	  PARTY  FOR  DIRECT,	INDIRECT,	SPECIAL,   INCIDENTAL,	 OR *	  CONSEQUENTIAL   DAMAGES  ARISING	OUT  OF  THE  USE  OF  THIS *	  SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN *	  IF  THE  AUTHOR  HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH *	  DAMAGE. * *	  THE  AUTHOR  AND	DISTRIBUTORS  SPECIFICALLY	 DISCLAIM	ANY *	  WARRANTIES,  INCLUDING,  BUT	NOT  LIMITED  TO,  THE	IMPLIED *	  WARRANTIES  OF  MERCHANTABILITY,	FITNESS  FOR  A  PARTICULAR *	  PURPOSE,	AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON *	  AN "AS IS" BASIS, AND THE AUTHOR	AND  DISTRIBUTORS  HAVE  NO *	  OBLIGATION   TO	PROVIDE   MAINTENANCE,	 SUPPORT,  UPDATES, *	  ENHANCEMENTS, OR MODIFICATIONS. * **********************************************************************/#include "plpgsql.h"#include <ctype.h>#include "pl.tab.h"#include "access/heapam.h"#include "catalog/namespace.h"#include "catalog/pg_attribute.h"#include "catalog/pg_attrdef.h"#include "catalog/pg_class.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"#include "funcapi.h"#include "nodes/makefuncs.h"#include "parser/gramparse.h"#include "parser/parse_type.h"#include "tcop/tcopprot.h"#include "utils/array.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/memutils.h"#include "utils/syscache.h"/* ---------- * Variables in the parser that shouldn't go into plpgsql.h * ---------- */extern PLPGSQL_YYSTYPE plpgsql_yylval;/* ---------- * Our own local and global variables * ---------- */static int	datums_alloc;int			plpgsql_nDatums;PLpgSQL_datum **plpgsql_Datums;static int	datums_last = 0;int			plpgsql_error_lineno;char	   *plpgsql_error_funcname;bool		plpgsql_DumpExecTree = false;bool		plpgsql_check_syntax = false;PLpgSQL_function *plpgsql_curr_compile;/* A context appropriate for short-term allocs during compilation */MemoryContext compile_tmp_cxt;/* ---------- * Hash table for compiled functions * ---------- */static HTAB *plpgsql_HashTable = NULL;typedef struct plpgsql_hashent{	PLpgSQL_func_hashkey key;	PLpgSQL_function *function;} plpgsql_HashEnt;#define FUNCS_PER_USER		128 /* initial table size *//* ---------- * Lookup table for EXCEPTION condition names * ---------- */typedef struct{	const char *label;	int			sqlerrstate;} ExceptionLabelMap;static const ExceptionLabelMap exception_label_map[] = {#include "plerrcodes.h"	{NULL, 0}};/* ---------- * static prototypes * ---------- */static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,		   HeapTuple procTup,		   PLpgSQL_func_hashkey *hashkey,		   bool forValidator);static int fetchArgInfo(HeapTuple procTup,			 Oid **p_argtypes, char ***p_argnames,			 char **p_argmodes);static PLpgSQL_row *build_row_from_class(Oid classOid);static PLpgSQL_row *build_row_from_vars(PLpgSQL_variable **vars, int numvars);static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);static void compute_function_hashkey(FunctionCallInfo fcinfo,						 Form_pg_proc procStruct,						 PLpgSQL_func_hashkey *hashkey,						 bool forValidator);static void plpgsql_resolve_polymorphic_argtypes(int numargs,									 Oid *argtypes, char *argmodes,									 Node *call_expr, bool forValidator,									 const char *proname);static PLpgSQL_function *plpgsql_HashTableLookup(PLpgSQL_func_hashkey *func_key);static void plpgsql_HashTableInsert(PLpgSQL_function *function,						PLpgSQL_func_hashkey *func_key);static void plpgsql_HashTableDelete(PLpgSQL_function *function);static void delete_function(PLpgSQL_function *func);/* ---------- * plpgsql_compile		Make an execution tree for a PL/pgSQL function. * * If forValidator is true, we're only compiling for validation purposes, * and so some checks are skipped. * * Note: it's important for this to fall through quickly if the function * has already been compiled. * ---------- */PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator){	Oid			funcOid = fcinfo->flinfo->fn_oid;	HeapTuple	procTup;	Form_pg_proc procStruct;	PLpgSQL_function *function;	PLpgSQL_func_hashkey hashkey;	bool		hashkey_valid = false;	/*	 * Lookup the pg_proc tuple by Oid; we'll need it in any case	 */	procTup = SearchSysCache(PROCOID,							 ObjectIdGetDatum(funcOid),							 0, 0, 0);	if (!HeapTupleIsValid(procTup))		elog(ERROR, "cache lookup failed for function %u", funcOid);	procStruct = (Form_pg_proc) GETSTRUCT(procTup);	/*	 * See if there's already a cache entry for the current FmgrInfo. If not,	 * try to find one in the hash table.	 */	function = (PLpgSQL_function *) fcinfo->flinfo->fn_extra;	if (!function)	{		/* Compute hashkey using function signature and actual arg types */		compute_function_hashkey(fcinfo, procStruct, &hashkey, forValidator);		hashkey_valid = true;		/* And do the lookup */		function = plpgsql_HashTableLookup(&hashkey);	}	if (function)	{		/* We have a compiled function, but is it still valid? */		if (!(function->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&			  function->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data)))		{			/* Nope, drop the function and associated storage */			delete_function(function);			function = NULL;		}	}	/*	 * If the function wasn't found or was out-of-date, we have to compile it	 */	if (!function)	{		/*		 * Calculate hashkey if we didn't already; we'll need it to store the		 * completed function.		 */		if (!hashkey_valid)			compute_function_hashkey(fcinfo, procStruct, &hashkey,									 forValidator);		/*		 * Do the hard part.		 */		function = do_compile(fcinfo, procTup, &hashkey, forValidator);	}	ReleaseSysCache(procTup);	/*	 * Save pointer in FmgrInfo to avoid search on subsequent calls	 */	fcinfo->flinfo->fn_extra = (void *) function;	/*	 * Finally return the compiled function	 */	return function;}/* * This is the slow part of plpgsql_compile(). * * While compiling a function, the CurrentMemoryContext is the * per-function memory context of the function we are compiling. That * means a palloc() will allocate storage with the same lifetime as * the function itself. * * Because palloc()'d storage will not be immediately freed, temporary * allocations should either be performed in a short-lived memory * context or explicitly pfree'd. Since not all backend functions are * careful about pfree'ing their allocations, it is also wise to * switch into a short-term context before calling into the * backend. An appropriate context for performing short-term * allocations is the compile_tmp_cxt. */static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,		   HeapTuple procTup,		   PLpgSQL_func_hashkey *hashkey,		   bool forValidator){	Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);	int			functype = CALLED_AS_TRIGGER(fcinfo) ? T_TRIGGER : T_FUNCTION;	PLpgSQL_function *function;	Datum		prosrcdatum;	bool		isnull;	char	   *proc_source;	HeapTuple	typeTup;	Form_pg_type typeStruct;	PLpgSQL_variable *var;	PLpgSQL_rec *rec;	int			i;	ErrorContextCallback plerrcontext;	int			parse_rc;	Oid			rettypeid;	int			numargs;	int			num_in_args = 0;	int			num_out_args = 0;	Oid		   *argtypes;	char	  **argnames;	char	   *argmodes;	int		   *in_arg_varnos = NULL;	PLpgSQL_variable **out_arg_variables;	MemoryContext func_cxt;	/*	 * Setup the scanner input and error info.	We assume that this function	 * cannot be invoked recursively, so there's no need to save and restore	 * the static variables used here.	 */	prosrcdatum = SysCacheGetAttr(PROCOID, procTup,								  Anum_pg_proc_prosrc, &isnull);	if (isnull)		elog(ERROR, "null prosrc");	proc_source = DatumGetCString(DirectFunctionCall1(textout, prosrcdatum));	plpgsql_scanner_init(proc_source, functype);	plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname));	plpgsql_error_lineno = 0;	/*	 * Setup error traceback support for ereport()	 */	plerrcontext.callback = plpgsql_compile_error_callback;	plerrcontext.arg = forValidator ? proc_source : NULL;	plerrcontext.previous = error_context_stack;	error_context_stack = &plerrcontext;	/*	 * Initialize the compiler	 */	plpgsql_ns_init();	plpgsql_ns_push(NULL);	plpgsql_DumpExecTree = false;	datums_alloc = 128;	plpgsql_nDatums = 0;	/* This is short-lived, so needn't allocate in function's cxt */	plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc);	datums_last = 0;	/*	 * Do extra syntax checks when validating the function definition. We skip	 * this when actually compiling functions for execution, for performance	 * reasons.	 */	plpgsql_check_syntax = forValidator;	/*	 * Create the new function node. We allocate the function and all of its	 * compile-time storage (e.g. parse tree) in its own memory context. This	 * allows us to reclaim the function's storage cleanly.	 */	func_cxt = AllocSetContextCreate(TopMemoryContext,									 "PL/PgSQL function context",									 ALLOCSET_DEFAULT_MINSIZE,									 ALLOCSET_DEFAULT_INITSIZE,									 ALLOCSET_DEFAULT_MAXSIZE);	compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);	function = palloc0(sizeof(*function));	plpgsql_curr_compile = function;	function->fn_name = pstrdup(NameStr(procStruct->proname));	function->fn_oid = fcinfo->flinfo->fn_oid;	function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);	function->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data);	function->fn_functype = functype;	function->fn_cxt = func_cxt;	function->out_param_varno = -1;		/* set up for no OUT param */	switch (functype)	{		case T_FUNCTION:			/*			 * Fetch info about the procedure's parameters. Allocations aren't			 * needed permanently, so make them in tmp cxt.			 *			 * We also need to resolve any polymorphic input or output			 * argument types.	In validation mode we won't be able to, so we			 * arbitrarily assume we are dealing with integers.			 */			MemoryContextSwitchTo(compile_tmp_cxt);			numargs = fetchArgInfo(procTup, &argtypes, &argnames, &argmodes);			plpgsql_resolve_polymorphic_argtypes(numargs, argtypes, argmodes,												 fcinfo->flinfo->fn_expr,												 forValidator,												 plpgsql_error_funcname);			in_arg_varnos = (int *) palloc(numargs * sizeof(int));			out_arg_variables = (PLpgSQL_variable **) palloc(numargs * sizeof(PLpgSQL_variable *));			MemoryContextSwitchTo(func_cxt);			/*			 * Create the variables for the procedure's parameters.			 */			for (i = 0; i < numargs; i++)			{				char		buf[32];				Oid			argtypeid = argtypes[i];				char		argmode = argmodes ? argmodes[i] : PROARGMODE_IN;				PLpgSQL_type *argdtype;				PLpgSQL_variable *argvariable;				int			argitemtype;				/* Create $n name for variable */				snprintf(buf, sizeof(buf), "$%d", i + 1);				/* Create datatype info */				argdtype = plpgsql_build_datatype(argtypeid, -1);				/* Disallow pseudotype argument */				/* (note we already replaced ANYARRAY/ANYELEMENT) */				/* (build_variable would do this, but wrong message) */				if (argdtype->ttype != PLPGSQL_TTYPE_SCALAR &&					argdtype->ttype != PLPGSQL_TTYPE_ROW)					ereport(ERROR,							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),							 errmsg("plpgsql functions cannot take type %s",									format_type_be(argtypeid))));				/* Build variable and add to datum list */				argvariable = plpgsql_build_variable(buf, 0,													 argdtype, false);				if (argvariable->dtype == PLPGSQL_DTYPE_VAR)				{					argitemtype = PLPGSQL_NSTYPE_VAR;					/* input argument vars are forced to be CONSTANT */					if (argmode == PROARGMODE_IN)						((PLpgSQL_var *) argvariable)->isconst = true;				}				else				{					Assert(argvariable->dtype == PLPGSQL_DTYPE_ROW);					argitemtype = PLPGSQL_NSTYPE_ROW;				}				/* Remember arguments in appropriate arrays */				if (argmode == PROARGMODE_IN || argmode == PROARGMODE_INOUT)					in_arg_varnos[num_in_args++] = argvariable->dno;				if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_INOUT)					out_arg_variables[num_out_args++] = argvariable;				/* Add to namespace under the $n name */				plpgsql_ns_additem(argitemtype, argvariable->dno, buf);				/* If there's a name for the argument, make an alias */				if (argnames && argnames[i][0] != '\0')					plpgsql_ns_additem(argitemtype, argvariable->dno,									   argnames[i]);			}			/*			 * If there's just one OUT parameter, out_param_varno points			 * directly to it.	If there's more than one, build a row that			 * holds all of them.			 */			if (num_out_args == 1)				function->out_param_varno = out_arg_variables[0]->dno;			else if (num_out_args > 1)			{				PLpgSQL_row *row = build_row_from_vars(out_arg_variables,													   num_out_args);				plpgsql_adddatum((PLpgSQL_datum *) row);				function->out_param_varno = row->rowno;			}			/*			 * Check for a polymorphic returntype. If found, use the actual			 * returntype type from the caller's FuncExpr node, if we have			 * one.  (In validation mode we arbitrarily assume we are dealing			 * with integers.)			 *			 * Note: errcode is FEATURE_NOT_SUPPORTED because it should always			 * work; if it doesn't we're in some context that fails to make			 * the info available.			 */			rettypeid = procStruct->prorettype;			if (rettypeid == ANYARRAYOID || rettypeid == ANYELEMENTOID)			{				if (forValidator)				{					if (rettypeid == ANYARRAYOID)						rettypeid = INT4ARRAYOID;					else						rettypeid = INT4OID;				}				else				{					rettypeid = get_fn_expr_rettype(fcinfo->flinfo);					if (!OidIsValid(rettypeid))						ereport(ERROR,								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),							 errmsg("could not determine actual return type "									"for polymorphic function \"%s\"",									plpgsql_error_funcname)));				}			}			/*			 * Normal function has a defined returntype			 */			function->fn_rettype = rettypeid;			function->fn_retset = procStruct->proretset;			/*			 * Lookup the function's return type			 */			typeTup = SearchSysCache(TYPEOID,									 ObjectIdGetDatum(rettypeid),									 0, 0, 0);			if (!HeapTupleIsValid(typeTup))				elog(ERROR, "cache lookup failed for type %u", rettypeid);			typeStruct = (Form_pg_type) GETSTRUCT(typeTup);			/* Disallow pseudotype result, except VOID or RECORD */			/* (note we already replaced ANYARRAY/ANYELEMENT) */			if (typeStruct->typtype == 'p')			{				if (rettypeid == VOIDOID ||					rettypeid == RECORDOID)					 /* okay */ ;				else if (rettypeid == TRIGGEROID)					ereport(ERROR,							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),							 errmsg("trigger functions may only be called as triggers")));				else					ereport(ERROR,							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),							 errmsg("plpgsql functions cannot return type %s",									format_type_be(rettypeid))));			}			if (typeStruct->typrelid != InvalidOid ||				rettypeid == RECORDOID)				function->fn_retistuple = true;			else			{				function->fn_retbyval = typeStruct->typbyval;				function->fn_rettyplen = typeStruct->typlen;				function->fn_rettypioparam = getTypeIOParam(typeTup);				fmgr_info(typeStruct->typinput, &(function->fn_retinput));				/*				 * install $0 reference, but only for polymorphic return				 * types, and not when the return is specified through an				 * output parameter.				 */				if ((procStruct->prorettype == ANYARRAYOID ||					 procStruct->prorettype == ANYELEMENTOID) &&					num_out_args == 0)				{					(void) plpgsql_build_variable("$0", 0,												  build_datatype(typeTup, -1),												  true);				}			}			ReleaseSysCache(typeTup);			break;		case T_TRIGGER:			/* Trigger procedure's return type is unknown yet */			function->fn_rettype = InvalidOid;			function->fn_retbyval = false;			function->fn_retistuple = true;

⌨️ 快捷键说明

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