in src/postgres/src_pl_plpgsql_src_pl_comp.c [158:341]
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 plpgsql_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;
/*
* 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 inline code context",
ALLOCSET_DEFAULT_SIZES);
plpgsql_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_LABEL_BLOCK);
plpgsql_DumpExecTree = false;
plpgsql_start_datums();
/* 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;
plpgsql_finish_datums(function);
/*
* Pop the error context stack
*/
error_context_stack = plerrcontext.previous;
plpgsql_error_funcname = NULL;
plpgsql_check_syntax = false;
MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
plpgsql_compile_tmp_cxt = NULL;
return function;
}