/*-------------------------------------------------------------------- * Symbols referenced in this file: * - plpgsql_compile_inline * - plpgsql_error_funcname * - plpgsql_check_syntax * - plpgsql_curr_compile * - compile_tmp_cxt * - plpgsql_DumpExecTree * - plpgsql_nDatums * - plpgsql_Datums * - plpgsql_build_variable * - plpgsql_adddatum * - plpgsql_build_record * - build_row_from_class * - plpgsql_build_datatype * - plpgsql_parse_tripword * - plpgsql_parse_dblword * - plpgsql_parse_word * - plpgsql_add_initdatums * - plpgsql_recognize_err_condition * - exception_label_map * - plpgsql_parse_err_condition * - plpgsql_parse_wordtype * - plpgsql_parse_wordrowtype * - plpgsql_parse_cwordtype * - plpgsql_parse_cwordrowtype * - plpgsql_parse_result * - plpgsql_compile_error_callback * - datums_alloc * - datums_last * - add_dummy_return *-------------------------------------------------------------------- */ /*------------------------------------------------------------------------- * * pl_comp.c - Compiler part of the PL/pgSQL * procedural language * * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/pl/plpgsql/src/pl_comp.c * *------------------------------------------------------------------------- */ #include "plpgsql.h" #include #include "access/htup_details.h" #include "catalog/namespace.h" #include "catalog/pg_proc.h" #include "catalog/pg_proc_fn.h" #include "catalog/pg_type.h" #include "funcapi.h" #include "nodes/makefuncs.h" #include "parser/parse_type.h" #include "utils/builtins.h" #include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/rel.h" #include "utils/syscache.h" /* ---------- * Our own local and global variables * ---------- */ __thread PLpgSQL_stmt_block *plpgsql_parse_result; static int datums_alloc; __thread int plpgsql_nDatums; __thread PLpgSQL_datum **plpgsql_Datums; static int datums_last = 0; __thread char *plpgsql_error_funcname; __thread bool plpgsql_DumpExecTree = false; __thread bool plpgsql_check_syntax = false; __thread PLpgSQL_function *plpgsql_curr_compile; /* A context appropriate for short-term allocs during compilation */ __thread MemoryContext compile_tmp_cxt; /* ---------- * Hash table for compiled functions * ---------- */ 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" /* pgrminclude ignore */ {NULL, 0} }; /* ---------- * static prototypes * ---------- */ static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo, HeapTuple procTup, PLpgSQL_function *function, PLpgSQL_func_hashkey *hashkey, bool forValidator); static void plpgsql_compile_error_callback(void *arg); static void add_parameter_name(int itemtype, int itemno, const char *name); static void add_dummy_return(PLpgSQL_function *function); static Node *plpgsql_pre_column_ref(ParseState *pstate, ColumnRef *cref); static Node *plpgsql_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var); static Node *plpgsql_param_ref(ParseState *pstate, ParamRef *pref); static Node *resolve_column_ref(ParseState *pstate, PLpgSQL_expr *expr, ColumnRef *cref, bool error_if_no_field); static Node *make_datum_param(PLpgSQL_expr *expr, int dno, int location); 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, Oid collation); 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. * ---------- */ /* * 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. */ /* ---------- * plpgsql_compile_inline Make an execution tree for an anonymous code block. * * Note: this is generally parallel to do_compile(); is it worth trying to * merge the two? * * Note: we assume the block will be thrown away so there is no need to build * persistent data structures. * ---------- */ PLpgSQL_function * plpgsql_compile_inline(char *proc_source) { char *func_name = "inline_code_block"; PLpgSQL_function *function; ErrorContextCallback plerrcontext; PLpgSQL_variable *var; int parse_rc; MemoryContext func_cxt; int i; /* * 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. */ plpgsql_scanner_init(proc_source); plpgsql_error_funcname = func_name; /* * Setup error traceback support for ereport() */ plerrcontext.callback = plpgsql_compile_error_callback; plerrcontext.arg = proc_source; plerrcontext.previous = error_context_stack; error_context_stack = &plerrcontext; /* Do extra syntax checking if check_function_bodies is on */ plpgsql_check_syntax = check_function_bodies; /* Function struct does not live past current statement */ function = (PLpgSQL_function *) palloc0(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(CurrentMemoryContext, "PL/pgSQL function context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); compile_tmp_cxt = MemoryContextSwitchTo(func_cxt); function->fn_signature = pstrdup(func_name); function->fn_is_trigger = PLPGSQL_NOT_TRIGGER; function->fn_input_collation = InvalidOid; function->fn_cxt = func_cxt; function->out_param_varno = -1; /* set up for no OUT param */ function->resolve_option = plpgsql_variable_conflict; function->print_strict_params = plpgsql_print_strict_params; /* * don't do extra validation for inline code as we don't want to add spam * at runtime */ function->extra_warnings = 0; function->extra_errors = 0; plpgsql_ns_init(); plpgsql_ns_push(func_name); plpgsql_DumpExecTree = false; datums_alloc = 128; plpgsql_nDatums = 0; plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc); datums_last = 0; /* Set up as though in a function returning VOID */ function->fn_rettype = VOIDOID; function->fn_retset = false; function->fn_retistuple = false; /* a bit of hardwired knowledge about type VOID here */ function->fn_retbyval = true; function->fn_rettyplen = sizeof(int32); /* * Remember if function is STABLE/IMMUTABLE. XXX would it be better to * set this TRUE inside a read-only transaction? Not clear. */ function->fn_readonly = false; /* * Create the magic FOUND variable. */ var = plpgsql_build_variable("found", 0, plpgsql_build_datatype(BOOLOID, -1, InvalidOid), true); function->found_varno = var->dno; /* * Now parse the function's text */ parse_rc = plpgsql_yyparse(); if (parse_rc != 0) elog(ERROR, "plpgsql parser returned %d", parse_rc); function->action = plpgsql_parse_result; plpgsql_scanner_finish(); /* * If it returns VOID (always true at the moment), we allow control to * fall off the end without an explicit RETURN statement. */ if (function->fn_rettype == VOIDOID) add_dummy_return(function); /* * Complete the function's info */ function->fn_nargs = 0; function->ndatums = plpgsql_nDatums; function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums); for (i = 0; i < plpgsql_nDatums; i++) function->datums[i] = plpgsql_Datums[i]; /* * Pop the error context stack */ error_context_stack = plerrcontext.previous; plpgsql_error_funcname = NULL; plpgsql_check_syntax = false; MemoryContextSwitchTo(compile_tmp_cxt); compile_tmp_cxt = NULL; return function; } /* * error context callback to let us supply a call-stack traceback. * If we are validating or executing an anonymous code block, the function * source text is passed as an argument. */ static void plpgsql_compile_error_callback(void *arg) { if (arg) { /* * Try to convert syntax error position to reference text of original * CREATE FUNCTION or DO command. */ if (function_parse_error_transpose((const char *) arg)) return; /* * Done if a syntax error position was reported; otherwise we have to * fall back to a "near line N" report. */ } if (plpgsql_error_funcname) errcontext("compilation of PL/pgSQL function \"%s\" near line %d", plpgsql_error_funcname, plpgsql_latest_lineno()); } /* * Add a name for a function parameter to the function's namespace */ /* * Add a dummy RETURN statement to the given function's body */ static void add_dummy_return(PLpgSQL_function *function) { /* * If the outer block has an EXCEPTION clause, we need to make a new outer * block, since the added RETURN shouldn't act like it is inside the * EXCEPTION clause. */ if (function->action->exceptions != NULL) { PLpgSQL_stmt_block *new; new = palloc0(sizeof(PLpgSQL_stmt_block)); new->cmd_type = PLPGSQL_STMT_BLOCK; new->body = list_make1(function->action); function->action = new; } if (function->action->body == NIL || ((PLpgSQL_stmt *) llast(function->action->body))->cmd_type != PLPGSQL_STMT_RETURN) { PLpgSQL_stmt_return *new; new = palloc0(sizeof(PLpgSQL_stmt_return)); new->cmd_type = PLPGSQL_STMT_RETURN; new->expr = NULL; new->retvarno = function->out_param_varno; function->action->body = lappend(function->action->body, new); } } /* * plpgsql_parser_setup set up parser hooks for dynamic parameters * * Note: this routine, and the hook functions it prepares for, are logically * part of plpgsql parsing. But they actually run during function execution, * when we are ready to evaluate a SQL query or expression that has not * previously been parsed and planned. */ /* * plpgsql_pre_column_ref parser callback before parsing a ColumnRef */ /* * plpgsql_post_column_ref parser callback after parsing a ColumnRef */ /* * plpgsql_param_ref parser callback for ParamRefs ($n symbols) */ /* * resolve_column_ref attempt to resolve a ColumnRef as a plpgsql var * * Returns the translated node structure, or NULL if name not found * * error_if_no_field tells whether to throw error or quietly return NULL if * we are able to match a record/row name but don't find a field name match. */ /* * Helper for columnref parsing: build a Param referencing a plpgsql datum, * and make sure that that datum is listed in the expression's paramnos. */ /* ---------- * plpgsql_parse_word The scanner calls this to postparse * any single word that is not a reserved keyword. * * word1 is the downcased/dequoted identifier; it must be palloc'd in the * function's long-term memory context. * * yytxt is the original token text; we need this to check for quoting, * so that later checks for unreserved keywords work properly. * * If recognized as a variable, fill in *wdatum and return TRUE; * if not recognized, fill in *word and return FALSE. * (Note: those two pointers actually point to members of the same union, * but for notational reasons we pass them separately.) * ---------- */ bool plpgsql_parse_word(char *word1, const char *yytxt, PLwdatum *wdatum, PLword *word) { PLpgSQL_nsitem *ns; /* * We should do nothing in DECLARE sections. In SQL expressions, there's * no need to do anything either --- lookup will happen when the * expression is compiled. */ if (plpgsql_IdentifierLookup == IDENTIFIER_LOOKUP_NORMAL) { /* * Do a lookup in the current namespace stack */ ns = plpgsql_ns_lookup(plpgsql_ns_top(), false, word1, NULL, NULL, NULL); if (ns != NULL) { switch (ns->itemtype) { case PLPGSQL_NSTYPE_VAR: case PLPGSQL_NSTYPE_ROW: case PLPGSQL_NSTYPE_REC: wdatum->datum = plpgsql_Datums[ns->itemno]; wdatum->ident = word1; wdatum->quoted = (yytxt[0] == '"'); wdatum->idents = NIL; return true; default: /* plpgsql_ns_lookup should never return anything else */ elog(ERROR, "unrecognized plpgsql itemtype: %d", ns->itemtype); } } } /* * Nothing found - up to now it's a word without any special meaning for * us. */ word->ident = word1; word->quoted = (yytxt[0] == '"'); return false; } /* ---------- * plpgsql_parse_dblword Same lookup for two words * separated by a dot. * ---------- */ bool plpgsql_parse_dblword(char *word1, char *word2, PLwdatum *wdatum, PLcword *cword) { PLpgSQL_nsitem *ns; List *idents; int nnames; idents = list_make2(makeString(word1), makeString(word2)); /* * We should do nothing in DECLARE sections. In SQL expressions, we * really only need to make sure that RECFIELD datums are created when * needed. */ if (plpgsql_IdentifierLookup != IDENTIFIER_LOOKUP_DECLARE) { /* * Do a lookup in the current namespace stack */ ns = plpgsql_ns_lookup(plpgsql_ns_top(), false, word1, word2, NULL, &nnames); if (ns != NULL) { switch (ns->itemtype) { case PLPGSQL_NSTYPE_VAR: /* Block-qualified reference to scalar variable. */ wdatum->datum = plpgsql_Datums[ns->itemno]; wdatum->ident = NULL; wdatum->quoted = false; /* not used */ wdatum->idents = idents; return true; case PLPGSQL_NSTYPE_REC: if (nnames == 1) { /* * First word is a record name, so second word could * be a field in this record. We build a RECFIELD * datum whether it is or not --- any error will be * detected later. */ PLpgSQL_recfield *new; new = palloc(sizeof(PLpgSQL_recfield)); new->dtype = PLPGSQL_DTYPE_RECFIELD; new->fieldname = pstrdup(word2); new->recparentno = ns->itemno; plpgsql_adddatum((PLpgSQL_datum *) new); wdatum->datum = (PLpgSQL_datum *) new; } else { /* Block-qualified reference to record variable. */ wdatum->datum = plpgsql_Datums[ns->itemno]; } wdatum->ident = NULL; wdatum->quoted = false; /* not used */ wdatum->idents = idents; return true; case PLPGSQL_NSTYPE_ROW: if (nnames == 1) { /* * First word is a row name, so second word could be a * field in this row. Again, no error now if it * isn't. */ PLpgSQL_row *row; int i; row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]); for (i = 0; i < row->nfields; i++) { if (row->fieldnames[i] && strcmp(row->fieldnames[i], word2) == 0) { wdatum->datum = plpgsql_Datums[row->varnos[i]]; wdatum->ident = NULL; wdatum->quoted = false; /* not used */ wdatum->idents = idents; return true; } } /* fall through to return CWORD */ } else { /* Block-qualified reference to row variable. */ wdatum->datum = plpgsql_Datums[ns->itemno]; wdatum->ident = NULL; wdatum->quoted = false; /* not used */ wdatum->idents = idents; return true; } break; default: break; } } } /* Nothing found */ cword->idents = idents; return false; } /* ---------- * plpgsql_parse_tripword Same lookup for three words * separated by dots. * ---------- */ bool plpgsql_parse_tripword(char *word1, char *word2, char *word3, PLwdatum *wdatum, PLcword *cword) { PLpgSQL_nsitem *ns; List *idents; int nnames; idents = list_make3(makeString(word1), makeString(word2), makeString(word3)); /* * We should do nothing in DECLARE sections. In SQL expressions, we * really only need to make sure that RECFIELD datums are created when * needed. */ if (plpgsql_IdentifierLookup != IDENTIFIER_LOOKUP_DECLARE) { /* * Do a lookup in the current namespace stack. Must find a qualified * reference, else ignore. */ ns = plpgsql_ns_lookup(plpgsql_ns_top(), false, word1, word2, word3, &nnames); if (ns != NULL && nnames == 2) { switch (ns->itemtype) { case PLPGSQL_NSTYPE_REC: { /* * words 1/2 are a record name, so third word could be * a field in this record. */ PLpgSQL_recfield *new; new = palloc(sizeof(PLpgSQL_recfield)); new->dtype = PLPGSQL_DTYPE_RECFIELD; new->fieldname = pstrdup(word3); new->recparentno = ns->itemno; plpgsql_adddatum((PLpgSQL_datum *) new); wdatum->datum = (PLpgSQL_datum *) new; wdatum->ident = NULL; wdatum->quoted = false; /* not used */ wdatum->idents = idents; return true; } case PLPGSQL_NSTYPE_ROW: { /* * words 1/2 are a row name, so third word could be a * field in this row. */ PLpgSQL_row *row; int i; row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]); for (i = 0; i < row->nfields; i++) { if (row->fieldnames[i] && strcmp(row->fieldnames[i], word3) == 0) { wdatum->datum = plpgsql_Datums[row->varnos[i]]; wdatum->ident = NULL; wdatum->quoted = false; /* not used */ wdatum->idents = idents; return true; } } /* fall through to return CWORD */ break; } default: break; } } } /* Nothing found */ cword->idents = idents; return false; } /* ---------- * plpgsql_parse_wordtype The scanner found word%TYPE. word can be * a variable name or a basetype. * * Returns datatype struct, or NULL if no match found for word. * ---------- */ PLpgSQL_type * plpgsql_parse_wordtype(char *ident) { return NULL; } /* ---------- * plpgsql_parse_cwordtype Same lookup for compositeword%TYPE * ---------- */ PLpgSQL_type * plpgsql_parse_cwordtype(List *idents) { return NULL; } /* ---------- * plpgsql_parse_wordrowtype Scanner found word%ROWTYPE. * So word must be a table name. * ---------- */ PLpgSQL_type * plpgsql_parse_wordrowtype(char *ident) { return NULL; } /* ---------- * plpgsql_parse_cwordrowtype Scanner found compositeword%ROWTYPE. * So word must be a namespace qualified table name. * ---------- */ PLpgSQL_type * plpgsql_parse_cwordrowtype(List *idents) { return NULL; } /* * plpgsql_build_variable - build a datum-array entry of a given * datatype * * The returned struct may be a PLpgSQL_var, PLpgSQL_row, or * PLpgSQL_rec depending on the given datatype, and is allocated via * palloc. The struct is automatically added to the current datum * array, and optionally to the current namespace. */ PLpgSQL_variable * plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype, bool add2namespace) { PLpgSQL_variable *result; switch (dtype->ttype) { case PLPGSQL_TTYPE_SCALAR: { /* Ordinary scalar datatype */ PLpgSQL_var *var; var = palloc0(sizeof(PLpgSQL_var)); var->dtype = PLPGSQL_DTYPE_VAR; var->refname = pstrdup(refname); var->lineno = lineno; var->datatype = dtype; /* other fields might be filled by caller */ /* preset to NULL */ var->value = 0; var->isnull = true; var->freeval = false; plpgsql_adddatum((PLpgSQL_datum *) var); if (add2namespace) plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->dno, refname); result = (PLpgSQL_variable *) var; break; } case PLPGSQL_TTYPE_ROW: { /* Composite type -- build a row variable */ PLpgSQL_row *row; row = build_row_from_class(dtype->typrelid); row->dtype = PLPGSQL_DTYPE_ROW; row->refname = pstrdup(refname); row->lineno = lineno; plpgsql_adddatum((PLpgSQL_datum *) row); if (add2namespace) plpgsql_ns_additem(PLPGSQL_NSTYPE_ROW, row->dno, refname); result = (PLpgSQL_variable *) row; break; } case PLPGSQL_TTYPE_REC: { /* "record" type -- build a record variable */ PLpgSQL_rec *rec; rec = plpgsql_build_record(refname, lineno, add2namespace); result = (PLpgSQL_variable *) rec; break; } case PLPGSQL_TTYPE_PSEUDO: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("variable \"%s\" has pseudo-type %s", refname, format_type_be(dtype->typoid)))); result = NULL; /* keep compiler quiet */ break; default: elog(ERROR, "unrecognized ttype: %d", dtype->ttype); result = NULL; /* keep compiler quiet */ break; } return result; } /* * Build empty named record variable, and optionally add it to namespace */ PLpgSQL_rec * plpgsql_build_record(const char *refname, int lineno, bool add2namespace) { PLpgSQL_rec *rec; rec = palloc0(sizeof(PLpgSQL_rec)); rec->dtype = PLPGSQL_DTYPE_REC; rec->refname = pstrdup(refname); rec->lineno = lineno; rec->tup = NULL; rec->tupdesc = NULL; rec->freetup = false; plpgsql_adddatum((PLpgSQL_datum *) rec); if (add2namespace) plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->dno, rec->refname); return rec; } /* * Build a row-variable data structure given the pg_class OID. */ static PLpgSQL_row *build_row_from_class(Oid classOid) { return NULL; } /* * Build a row-variable data structure given the component variables. */ /* * plpgsql_build_datatype * Build PLpgSQL_type struct given type OID, typmod, and collation. * * If collation is not InvalidOid then it overrides the type's default * collation. But collation is ignored if the datatype is non-collatable. */ PLpgSQL_type * plpgsql_build_datatype(Oid typeOid, int32 typmod, Oid collation) { PLpgSQL_type *typ; typ = (PLpgSQL_type *) palloc0(sizeof(PLpgSQL_type)); typ->typname = pstrdup("UNKNOWN"); typ->ttype = PLPGSQL_TTYPE_SCALAR; return typ; } /* * Utility subroutine to make a PLpgSQL_type struct given a pg_type entry */ /* * plpgsql_recognize_err_condition * Check condition name and translate it to SQLSTATE. * * Note: there are some cases where the same condition name has multiple * entries in the table. We arbitrarily return the first match. */ int plpgsql_recognize_err_condition(const char *condname, bool allow_sqlstate) { int i; if (allow_sqlstate) { if (strlen(condname) == 5 && strspn(condname, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5) return MAKE_SQLSTATE(condname[0], condname[1], condname[2], condname[3], condname[4]); } for (i = 0; exception_label_map[i].label != NULL; i++) { if (strcmp(condname, exception_label_map[i].label) == 0) return exception_label_map[i].sqlerrstate; } ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unrecognized exception condition \"%s\"", condname))); return 0; /* keep compiler quiet */ } /* * plpgsql_parse_err_condition * Generate PLpgSQL_condition entry(s) for an exception condition name * * This has to be able to return a list because there are some duplicate * names in the table of error code names. */ PLpgSQL_condition * plpgsql_parse_err_condition(char *condname) { int i; PLpgSQL_condition *new; PLpgSQL_condition *prev; /* * XXX Eventually we will want to look for user-defined exception names * here. */ /* * OTHERS is represented as code 0 (which would map to '00000', but we * have no need to represent that as an exception condition). */ if (strcmp(condname, "others") == 0) { new = palloc(sizeof(PLpgSQL_condition)); new->sqlerrstate = 0; new->condname = condname; new->next = NULL; return new; } prev = NULL; for (i = 0; exception_label_map[i].label != NULL; i++) { if (strcmp(condname, exception_label_map[i].label) == 0) { new = palloc(sizeof(PLpgSQL_condition)); new->sqlerrstate = exception_label_map[i].sqlerrstate; new->condname = condname; new->next = prev; prev = new; } } if (!prev) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unrecognized exception condition \"%s\"", condname))); return prev; } /* ---------- * plpgsql_adddatum Add a variable, record or row * to the compiler's datum list. * ---------- */ void plpgsql_adddatum(PLpgSQL_datum *new) { if (plpgsql_nDatums == datums_alloc) { datums_alloc *= 2; plpgsql_Datums = repalloc(plpgsql_Datums, sizeof(PLpgSQL_datum *) * datums_alloc); } new->dno = plpgsql_nDatums; plpgsql_Datums[plpgsql_nDatums++] = new; } /* ---------- * plpgsql_add_initdatums Make an array of the datum numbers of * all the simple VAR datums created since the last call * to this function. * * If varnos is NULL, we just forget any datum entries created since the * last call. * * This is used around a DECLARE section to create a list of the VARs * that have to be initialized at block entry. Note that VARs can also * be created elsewhere than DECLARE, eg by a FOR-loop, but it is then * the responsibility of special-purpose code to initialize them. * ---------- */ int plpgsql_add_initdatums(int **varnos) { int i; int n = 0; for (i = datums_last; i < plpgsql_nDatums; i++) { switch (plpgsql_Datums[i]->dtype) { case PLPGSQL_DTYPE_VAR: n++; break; default: break; } } if (varnos != NULL) { if (n > 0) { *varnos = (int *) palloc(sizeof(int) * n); n = 0; for (i = datums_last; i < plpgsql_nDatums; i++) { switch (plpgsql_Datums[i]->dtype) { case PLPGSQL_DTYPE_VAR: (*varnos)[n++] = plpgsql_Datums[i]->dno; default: break; } } } else *varnos = NULL; } datums_last = plpgsql_nDatums; return n; } /* * Compute the hashkey for a given function invocation * * The hashkey is returned into the caller-provided storage at *hashkey. */ /* * This is the same as the standard resolve_polymorphic_argtypes() function, * but with a special case for validation: assume that polymorphic arguments * are integer, integer-array or integer-range. Also, we go ahead and report * the error if we can't resolve the types. */ /* * delete_function - clean up as much as possible of a stale function cache * * We can't release the PLpgSQL_function struct itself, because of the * possibility that there are fn_extra pointers to it. We can release * the subsidiary storage, but only if there are no active evaluations * in progress. Otherwise we'll just leak that storage. Since the * case would only occur if a pg_proc update is detected during a nested * recursive call on the function, a leak seems acceptable. * * Note that this can be called more than once if there are multiple fn_extra * pointers to the same function cache. Hence be careful not to do things * twice. */ /* exported so we can call it from plpgsql_init() */