pl_comp.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,081 行 · 第 1/4 页

C
2,081
字号
/*------------------------------------------------------------------------- * * pl_comp.c		- Compiler part of the PL/pgSQL *			  procedural language * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.121 2008/01/01 19:46:00 momjian Exp $ * *------------------------------------------------------------------------- */#include "plpgsql.h"#include <ctype.h>#include "pl.tab.h"#include "access/heapam.h"#include "catalog/namespace.h"#include "catalog/pg_attrdef.h"#include "catalog/pg_attribute.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"/* ---------- * 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_function *function,		   PLpgSQL_func_hashkey *hashkey,		   bool forValidator);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		function_valid = false;	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;recheck:	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) &&			ItemPointerEquals(&function->fn_tid, &procTup->t_self))			function_valid = true;		else		{			/*			 * Nope, so remove it from hashtable and try to drop associated			 * storage (if not done already).			 */			delete_function(function);			/*			 * If the function isn't in active use then we can overwrite the			 * func struct with new data, allowing any other existing fn_extra			 * pointers to make use of the new definition on their next use.			 * If it is in use then just leave it alone and make a new one.			 * (The active invocations will run to completion using the			 * previous definition, and then the cache entry will just be			 * leaked; doesn't seem worth adding code to clean it up, given			 * what a corner case this is.)			 *			 * If we found the function struct via fn_extra then it's possible			 * a replacement has already been made, so go back and recheck the			 * hashtable.			 */			if (function->use_count != 0)			{				function = NULL;				if (!hashkey_valid)					goto recheck;			}		}	}	/*	 * If the function wasn't found or was out-of-date, we have to compile it	 */	if (!function_valid)	{		/*		 * 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, function,							  &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(). * * The passed-in "function" pointer is either NULL or an already-allocated * function struct to overwrite. * * 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. * * NB: this code is not re-entrant.  We assume that nothing we do here could * result in the invocation of another plpgsql function. */static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,		   HeapTuple procTup,		   PLpgSQL_function *function,		   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;	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, particularly the namespace stack.  The	 * outermost namespace contains function parameters and other special	 * variables (such as FOUND), and is named after the function itself.	 */	plpgsql_ns_init();	plpgsql_ns_push(NameStr(procStruct->proname));	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 struct, if not done already.  The function	 * structs are never thrown away, so keep them in TopMemoryContext.	 */	if (function == NULL)	{		function = (PLpgSQL_function *)			MemoryContextAllocZero(TopMemoryContext, sizeof(PLpgSQL_function));	}	else	{		/* re-using a previously existing struct, so clear it out */		memset(function, 0, sizeof(PLpgSQL_function));	}	plpgsql_curr_compile = function;	/*	 * All the rest of the compile-time storage (e.g. parse tree) is kept in	 * its own memory context, so it can be reclaimed easily.	 */	func_cxt = AllocSetContextCreate(TopMemoryContext,									 "PL/PgSQL function context",									 ALLOCSET_DEFAULT_MINSIZE,									 ALLOCSET_DEFAULT_INITSIZE,									 ALLOCSET_DEFAULT_MAXSIZE);	compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);	function->fn_name = pstrdup(NameStr(procStruct->proname));	function->fn_oid = fcinfo->flinfo->fn_oid;	function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);	function->fn_tid = procTup->t_self;	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 = get_func_arg_info(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 polymorphic types) */				/* (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 (IsPolymorphicType(rettypeid))			{				if (forValidator)				{					if (rettypeid == ANYARRAYOID)						rettypeid = INT4ARRAYOID;					else	/* ANYELEMENT or ANYNONARRAY */						rettypeid = INT4OID;					/* XXX what could we use for ANYENUM? */				}				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 polymorphic types) */			if (typeStruct->typtype == TYPTYPE_PSEUDO)			{

⌨️ 快捷键说明

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