📄 pl_comp.c
字号:
/********************************************************************** * 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 + -