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 + -
显示快捷键?