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

📄 pl_comp.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 3 页
字号:
/********************************************************************** * pl_comp.c		- Compiler part of the PL/pgSQL *			  procedural language * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.69 2003/09/30 00:59:51 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 <setjmp.h>#include "pl.tab.h"#include "access/heapam.h"#include "catalog/catname.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 "nodes/makefuncs.h"#include "parser/gramparse.h"#include "parser/parse_type.h"#include "tcop/tcopprot.h"#include "utils/builtins.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;int			plpgsql_DumpExecTree = 0;PLpgSQL_function *plpgsql_curr_compile;/* ---------- * Hash table for compiled functions * ---------- */static HTAB *plpgsql_HashTable = (HTAB *) NULL;typedef struct plpgsql_hashent{	PLpgSQL_func_hashkey key;	PLpgSQL_function *function;}	plpgsql_HashEnt;#define FUNCS_PER_USER		128 /* initial table size *//* ---------- * static prototypes * ---------- */static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,		   HeapTuple procTup,		   PLpgSQL_func_hashkey *hashkey);static void plpgsql_compile_error_callback(void *arg);static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);static void compute_function_hashkey(FunctionCallInfo fcinfo,						 Form_pg_proc procStruct,						 PLpgSQL_func_hashkey *hashkey);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);/* * This routine is a crock, and so is everyplace that calls it.  The problem * is that the compiled form of a plpgsql function is allocated permanently * (mostly via malloc()) and never released until backend exit.  Subsidiary * data structures such as fmgr info records therefore must live forever * as well.  A better implementation would store all this stuff in a per- * function memory context that could be reclaimed at need.  In the meantime, * fmgr_info_cxt must be called specifying TopMemoryContext so that whatever * it might allocate, and whatever the eventual function might allocate using * fn_mcxt, will live forever too. */static voidperm_fmgr_info(Oid functionId, FmgrInfo *finfo){	fmgr_info_cxt(functionId, finfo, TopMemoryContext);}/* ---------- * plpgsql_compile		Make an execution tree for a PL/pgSQL function. * * Note: it's important for this to fall through quickly if the function * has already been compiled. * ---------- */PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo){	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)	{		/* First time through in this backend?	If so, init hashtable */		if (!plpgsql_HashTable)			plpgsql_HashTableInit();		/* Compute hashkey using function signature and actual arg types */		compute_function_hashkey(fcinfo, procStruct, &hashkey);		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 hashtable entry.	XXX someday, free all the			 * subsidiary storage as well.			 */			plpgsql_HashTableDelete(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);		/*		 * Do the hard part.		 */		function = do_compile(fcinfo, procTup, &hashkey);	}	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(). */static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,		   HeapTuple procTup,		   PLpgSQL_func_hashkey *hashkey){	Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);	int			functype = CALLED_AS_TRIGGER(fcinfo) ? T_TRIGGER : T_FUNCTION;	PLpgSQL_function *function;	char	   *proc_source;	HeapTuple	typeTup;	Form_pg_type typeStruct;	PLpgSQL_var *var;	PLpgSQL_row *row;	PLpgSQL_rec *rec;	int			i;	int			arg_varnos[FUNC_MAX_ARGS];	ErrorContextCallback plerrcontext;	int			parse_rc;	Oid			rettypeid;	/*	 * 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.	 */	proc_source = DatumGetCString(DirectFunctionCall1(textout,								  PointerGetDatum(&procStruct->prosrc)));	plpgsql_scanner_init(proc_source, functype);	pfree(proc_source);	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 = NULL;	plerrcontext.previous = error_context_stack;	error_context_stack = &plerrcontext;	/*	 * Initialize the compiler	 */	plpgsql_ns_init();	plpgsql_ns_push(NULL);	plpgsql_DumpExecTree = 0;	datums_alloc = 128;	plpgsql_nDatums = 0;	plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc);	datums_last = 0;	/*	 * Create the new function node	 */	function = malloc(sizeof(PLpgSQL_function));	MemSet(function, 0, sizeof(PLpgSQL_function));	plpgsql_curr_compile = function;	function->fn_name = strdup(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;	switch (functype)	{		case T_FUNCTION:			/*			 * Check for a polymorphic returntype. If found, use the			 * actual returntype type from the caller's FuncExpr node, if			 * we have one.			 *			 * 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)			{				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 functions 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_rettypelem = typeStruct->typelem;				perm_fmgr_info(typeStruct->typinput, &(function->fn_retinput));				/*				 * install $0 reference, but only for polymorphic return				 * types				 */				if (procStruct->prorettype == ANYARRAYOID ||					procStruct->prorettype == ANYELEMENTOID)				{					char		buf[32];					/* name for variable */					snprintf(buf, sizeof(buf), "$%d", 0);					/*					 * Normal return values get a var node					 */					var = malloc(sizeof(PLpgSQL_var));					memset(var, 0, sizeof(PLpgSQL_var));					var->dtype = PLPGSQL_DTYPE_VAR;					var->refname = strdup(buf);					var->lineno = 0;					var->datatype = build_datatype(typeTup, -1);					var->isconst = false;					var->notnull = false;					var->default_val = NULL;					/* preset to NULL */					var->value = 0;					var->isnull = true;					var->freeval = false;					plpgsql_adddatum((PLpgSQL_datum *) var);					plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno,									   var->refname);				}			}			ReleaseSysCache(typeTup);			/*			 * Create the variables for the procedure's parameters			 */			for (i = 0; i < procStruct->pronargs; i++)			{				char		buf[32];				Oid			argtypeid;				/* name for variable */				snprintf(buf, sizeof(buf), "$%d", i + 1);				/*				 * Since we already did the replacement of polymorphic				 * argument types by actual argument types while computing				 * the hashkey, we can just use those results.				 */				argtypeid = hashkey->argtypes[i];				/*				 * Get the parameters type				 */				typeTup = SearchSysCache(TYPEOID,										 ObjectIdGetDatum(argtypeid),										 0, 0, 0);				if (!HeapTupleIsValid(typeTup))					elog(ERROR, "cache lookup failed for type %u", argtypeid);				typeStruct = (Form_pg_type) GETSTRUCT(typeTup);				/* Disallow pseudotype argument */				/* (note we already replaced ANYARRAY/ANYELEMENT) */				if (typeStruct->typtype == 'p')					ereport(ERROR,							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),						  errmsg("plpgsql functions cannot take type %s",								 format_type_be(argtypeid))));				if (typeStruct->typrelid != InvalidOid)				{					/*					 * For tuple type parameters, we set up a record of					 * that type					 */					row = plpgsql_build_rowtype(typeStruct->typrelid);					row->refname = strdup(buf);					plpgsql_adddatum((PLpgSQL_datum *) row);					plpgsql_ns_additem(PLPGSQL_NSTYPE_ROW, row->rowno,									   row->refname);					arg_varnos[i] = row->rowno;				}				else				{					/*					 * Normal parameters get a var node					 */					var = malloc(sizeof(PLpgSQL_var));					memset(var, 0, sizeof(PLpgSQL_var));					var->dtype = PLPGSQL_DTYPE_VAR;					var->refname = strdup(buf);					var->lineno = 0;					var->datatype = build_datatype(typeTup, -1);					var->isconst = true;					var->notnull = false;					var->default_val = NULL;					plpgsql_adddatum((PLpgSQL_datum *) var);					plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno,									   var->refname);					arg_varnos[i] = var->varno;				}				ReleaseSysCache(typeTup);			}			break;		case T_TRIGGER:			/*			 * Trigger procedures return type is unknown yet			 */			function->fn_rettype = InvalidOid;			function->fn_retbyval = false;			function->fn_retistuple = true;			function->fn_retset = false;			/*			 * Add the record for referencing NEW			 */			rec = malloc(sizeof(PLpgSQL_rec));			memset(rec, 0, sizeof(PLpgSQL_rec));			rec->dtype = PLPGSQL_DTYPE_REC;			rec->refname = strdup("new");			rec->tup = NULL;			rec->tupdesc = NULL;			rec->freetup = false;			plpgsql_adddatum((PLpgSQL_datum *) rec);			plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname);			function->new_varno = rec->recno;			/*			 * Add the record for referencing OLD			 */			rec = malloc(sizeof(PLpgSQL_rec));			memset(rec, 0, sizeof(PLpgSQL_rec));			rec->dtype = PLPGSQL_DTYPE_REC;			rec->refname = strdup("old");			rec->tup = NULL;			rec->tupdesc = NULL;			rec->freetup = false;			plpgsql_adddatum((PLpgSQL_datum *) rec);			plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname);			function->old_varno = rec->recno;			/*			 * Add the variable tg_name			 */			var = malloc(sizeof(PLpgSQL_var));			memset(var, 0, sizeof(PLpgSQL_var));			var->dtype = PLPGSQL_DTYPE_VAR;			var->refname = strdup("tg_name");			var->lineno = 0;			var->datatype = plpgsql_parse_datatype("name");			var->isconst = false;			var->notnull = false;			var->default_val = NULL;			plpgsql_adddatum((PLpgSQL_datum *) var);			plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);			function->tg_name_varno = var->varno;			/*			 * Add the variable tg_when			 */			var = malloc(sizeof(PLpgSQL_var));			memset(var, 0, sizeof(PLpgSQL_var));			var->dtype = PLPGSQL_DTYPE_VAR;			var->refname = strdup("tg_when");			var->lineno = 0;			var->datatype = plpgsql_parse_datatype("text");			var->isconst = false;			var->notnull = false;			var->default_val = NULL;			plpgsql_adddatum((PLpgSQL_datum *) var);			plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);			function->tg_when_varno = var->varno;			/*			 * Add the variable tg_level			 */			var = malloc(sizeof(PLpgSQL_var));			memset(var, 0, sizeof(PLpgSQL_var));			var->dtype = PLPGSQL_DTYPE_VAR;			var->refname = strdup("tg_level");			var->lineno = 0;			var->datatype = plpgsql_parse_datatype("text");			var->isconst = false;			var->notnull = false;			var->default_val = NULL;			plpgsql_adddatum((PLpgSQL_datum *) var);			plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);			function->tg_level_varno = var->varno;			/*			 * Add the variable tg_op			 */			var = malloc(sizeof(PLpgSQL_var));			memset(var, 0, sizeof(PLpgSQL_var));			var->dtype = PLPGSQL_DTYPE_VAR;			var->refname = strdup("tg_op");			var->lineno = 0;			var->datatype = plpgsql_parse_datatype("text");			var->isconst = false;			var->notnull = false;			var->default_val = NULL;			plpgsql_adddatum((PLpgSQL_datum *) var);			plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);			function->tg_op_varno = var->varno;			/*			 * Add the variable tg_relid			 */			var = malloc(sizeof(PLpgSQL_var));			memset(var, 0, sizeof(PLpgSQL_var));			var->dtype = PLPGSQL_DTYPE_VAR;			var->refname = strdup("tg_relid");			var->lineno = 0;			var->datatype = plpgsql_parse_datatype("oid");			var->isconst = false;			var->notnull = false;			var->default_val = NULL;			plpgsql_adddatum((PLpgSQL_datum *) var);			plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);			function->tg_relid_varno = var->varno;			/*			 * Add the variable tg_relname			 */			var = malloc(sizeof(PLpgSQL_var));			memset(var, 0, sizeof(PLpgSQL_var));

⌨️ 快捷键说明

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