in src/pl/plpgsql/src/pl_exec.c [252:784]
static void coerce_function_result_tuple(PLpgSQL_execstate *estate,
TupleDesc tupdesc);
static void plpgsql_exec_error_callback(void *arg);
static void copy_plpgsql_datums(PLpgSQL_execstate *estate,
PLpgSQL_function *func);
static void plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
PLpgSQL_var *var);
static MemoryContext get_stmt_mcontext(PLpgSQL_execstate *estate);
static void push_stmt_mcontext(PLpgSQL_execstate *estate);
static void pop_stmt_mcontext(PLpgSQL_execstate *estate);
static int exec_toplevel_block(PLpgSQL_execstate *estate,
PLpgSQL_stmt_block *block);
static int exec_stmt_block(PLpgSQL_execstate *estate,
PLpgSQL_stmt_block *block);
static int exec_stmts(PLpgSQL_execstate *estate,
List *stmts);
static int exec_stmt_assign(PLpgSQL_execstate *estate,
PLpgSQL_stmt_assign *stmt);
static int exec_stmt_perform(PLpgSQL_execstate *estate,
PLpgSQL_stmt_perform *stmt);
static int exec_stmt_call(PLpgSQL_execstate *estate,
PLpgSQL_stmt_call *stmt);
static int exec_stmt_getdiag(PLpgSQL_execstate *estate,
PLpgSQL_stmt_getdiag *stmt);
static int exec_stmt_if(PLpgSQL_execstate *estate,
PLpgSQL_stmt_if *stmt);
static int exec_stmt_case(PLpgSQL_execstate *estate,
PLpgSQL_stmt_case *stmt);
static int exec_stmt_loop(PLpgSQL_execstate *estate,
PLpgSQL_stmt_loop *stmt);
static int exec_stmt_while(PLpgSQL_execstate *estate,
PLpgSQL_stmt_while *stmt);
static int exec_stmt_fori(PLpgSQL_execstate *estate,
PLpgSQL_stmt_fori *stmt);
static int exec_stmt_fors(PLpgSQL_execstate *estate,
PLpgSQL_stmt_fors *stmt);
static int exec_stmt_forc(PLpgSQL_execstate *estate,
PLpgSQL_stmt_forc *stmt);
static int exec_stmt_foreach_a(PLpgSQL_execstate *estate,
PLpgSQL_stmt_foreach_a *stmt);
static int exec_stmt_open(PLpgSQL_execstate *estate,
PLpgSQL_stmt_open *stmt);
static int exec_stmt_fetch(PLpgSQL_execstate *estate,
PLpgSQL_stmt_fetch *stmt);
static int exec_stmt_close(PLpgSQL_execstate *estate,
PLpgSQL_stmt_close *stmt);
static int exec_stmt_exit(PLpgSQL_execstate *estate,
PLpgSQL_stmt_exit *stmt);
static int exec_stmt_return(PLpgSQL_execstate *estate,
PLpgSQL_stmt_return *stmt);
static int exec_stmt_return_next(PLpgSQL_execstate *estate,
PLpgSQL_stmt_return_next *stmt);
static int exec_stmt_return_query(PLpgSQL_execstate *estate,
PLpgSQL_stmt_return_query *stmt);
static int exec_stmt_raise(PLpgSQL_execstate *estate,
PLpgSQL_stmt_raise *stmt);
static int exec_stmt_assert(PLpgSQL_execstate *estate,
PLpgSQL_stmt_assert *stmt);
static int exec_stmt_execsql(PLpgSQL_execstate *estate,
PLpgSQL_stmt_execsql *stmt);
static int exec_stmt_dynexecute(PLpgSQL_execstate *estate,
PLpgSQL_stmt_dynexecute *stmt);
static int exec_stmt_dynfors(PLpgSQL_execstate *estate,
PLpgSQL_stmt_dynfors *stmt);
static int exec_stmt_commit(PLpgSQL_execstate *estate,
PLpgSQL_stmt_commit *stmt);
static int exec_stmt_rollback(PLpgSQL_execstate *estate,
PLpgSQL_stmt_rollback *stmt);
static void plpgsql_estate_setup(PLpgSQL_execstate *estate,
PLpgSQL_function *func,
ReturnSetInfo *rsi,
EState *simple_eval_estate,
ResourceOwner simple_eval_resowner);
static void exec_eval_cleanup(PLpgSQL_execstate *estate);
static void exec_prepare_plan(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr, int cursorOptions);
static void exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr);
static void exec_save_simple_expr(PLpgSQL_expr *expr, CachedPlan *cplan);
static void exec_check_rw_parameter(PLpgSQL_expr *expr);
static bool exec_eval_simple_expr(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr,
Datum *result,
bool *isNull,
Oid *rettype,
int32 *rettypmod);
static void exec_assign_expr(PLpgSQL_execstate *estate,
PLpgSQL_datum *target,
PLpgSQL_expr *expr);
static void exec_assign_c_string(PLpgSQL_execstate *estate,
PLpgSQL_datum *target,
const char *str);
static void exec_assign_value(PLpgSQL_execstate *estate,
PLpgSQL_datum *target,
Datum value, bool isNull,
Oid valtype, int32 valtypmod);
static void exec_eval_datum(PLpgSQL_execstate *estate,
PLpgSQL_datum *datum,
Oid *typeid,
int32 *typetypmod,
Datum *value,
bool *isnull);
static int exec_eval_integer(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr,
bool *isNull);
static bool exec_eval_boolean(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr,
bool *isNull);
static Datum exec_eval_expr(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr,
bool *isNull,
Oid *rettype,
int32 *rettypmod);
static int exec_run_select(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr, long maxtuples, Portal *portalP);
static int exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
Portal portal, bool prefetch_ok);
static ParamListInfo setup_param_list(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr);
static ParamExternData *plpgsql_param_fetch(ParamListInfo params,
int paramid, bool speculative,
ParamExternData *workspace);
static void plpgsql_param_compile(ParamListInfo params, Param *param,
ExprState *state,
Datum *resv, bool *resnull);
static void plpgsql_param_eval_var(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
static void plpgsql_param_eval_var_ro(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
static void plpgsql_param_eval_recfield(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
static void plpgsql_param_eval_generic(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
static void plpgsql_param_eval_generic_ro(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
static void exec_move_row(PLpgSQL_execstate *estate,
PLpgSQL_variable *target,
HeapTuple tup, TupleDesc tupdesc);
static void revalidate_rectypeid(PLpgSQL_rec *rec);
static ExpandedRecordHeader *make_expanded_record_for_rec(PLpgSQL_execstate *estate,
PLpgSQL_rec *rec,
TupleDesc srctupdesc,
ExpandedRecordHeader *srcerh);
static void exec_move_row_from_fields(PLpgSQL_execstate *estate,
PLpgSQL_variable *target,
ExpandedRecordHeader *newerh,
Datum *values, bool *nulls,
TupleDesc tupdesc);
static bool compatible_tupdescs(TupleDesc src_tupdesc, TupleDesc dst_tupdesc);
static HeapTuple make_tuple_from_row(PLpgSQL_execstate *estate,
PLpgSQL_row *row,
TupleDesc tupdesc);
static TupleDesc deconstruct_composite_datum(Datum value,
HeapTupleData *tmptup);
static void exec_move_row_from_datum(PLpgSQL_execstate *estate,
PLpgSQL_variable *target,
Datum value);
static void instantiate_empty_record_variable(PLpgSQL_execstate *estate,
PLpgSQL_rec *rec);
static char *convert_value_to_string(PLpgSQL_execstate *estate,
Datum value, Oid valtype);
static inline Datum exec_cast_value(PLpgSQL_execstate *estate,
Datum value, bool *isnull,
Oid valtype, int32 valtypmod,
Oid reqtype, int32 reqtypmod);
static Datum do_cast_value(PLpgSQL_execstate *estate,
Datum value, bool *isnull,
Oid valtype, int32 valtypmod,
Oid reqtype, int32 reqtypmod);
static plpgsql_CastHashEntry *get_cast_hashentry(PLpgSQL_execstate *estate,
Oid srctype, int32 srctypmod,
Oid dsttype, int32 dsttypmod);
static void exec_init_tuple_store(PLpgSQL_execstate *estate);
static void exec_set_found(PLpgSQL_execstate *estate, bool state);
static void plpgsql_create_econtext(PLpgSQL_execstate *estate);
static void plpgsql_destroy_econtext(PLpgSQL_execstate *estate);
static void assign_simple_var(PLpgSQL_execstate *estate, PLpgSQL_var *var,
Datum newvalue, bool isnull, bool freeable);
static void assign_text_var(PLpgSQL_execstate *estate, PLpgSQL_var *var,
const char *str);
static void assign_record_var(PLpgSQL_execstate *estate, PLpgSQL_rec *rec,
ExpandedRecordHeader *erh);
static ParamListInfo exec_eval_using_params(PLpgSQL_execstate *estate,
List *params);
static Portal exec_dynquery_with_params(PLpgSQL_execstate *estate,
PLpgSQL_expr *dynquery, List *params,
const char *portalname, int cursorOptions);
static char *format_expr_params(PLpgSQL_execstate *estate,
const PLpgSQL_expr *expr);
static char *format_preparedparamsdata(PLpgSQL_execstate *estate,
ParamListInfo paramLI);
static PLpgSQL_variable *make_callstmt_target(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr);
/* ----------
* plpgsql_exec_function Called by the call handler for
* function execution.
*
* This is also used to execute inline code blocks (DO blocks). The only
* difference that this code is aware of is that for a DO block, we want
* to use a private simple_eval_estate and a private simple_eval_resowner,
* which are created and passed in by the caller. For regular functions,
* pass NULL, which implies using shared_simple_eval_estate and
* shared_simple_eval_resowner. (When using a private simple_eval_estate,
* we must also use a private cast hashtable, but that's taken care of
* within plpgsql_estate_setup.)
* procedure_resowner is a resowner that will survive for the duration
* of execution of this function/procedure. It is needed only if we
* are doing non-atomic execution and there are CALL or DO statements
* in the function; otherwise it can be NULL. We use it to hold refcounts
* on the CALL/DO statements' plans.
* ----------
*/
Datum
plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
EState *simple_eval_estate,
ResourceOwner simple_eval_resowner,
ResourceOwner procedure_resowner,
bool atomic)
{
PLpgSQL_execstate estate;
ErrorContextCallback plerrcontext;
int i;
int rc;
/*
* Setup the execution state
*/
plpgsql_estate_setup(&estate, func, (ReturnSetInfo *) fcinfo->resultinfo,
simple_eval_estate, simple_eval_resowner);
estate.procedure_resowner = procedure_resowner;
estate.atomic = atomic;
/*
* Setup error traceback support for ereport()
*/
plerrcontext.callback = plpgsql_exec_error_callback;
plerrcontext.arg = &estate;
plerrcontext.previous = error_context_stack;
error_context_stack = &plerrcontext;
/*
* Make local execution copies of all the datums
*/
estate.err_text = gettext_noop("during initialization of execution state");
copy_plpgsql_datums(&estate, func);
/*
* Store the actual call argument values into the appropriate variables
*/
estate.err_text = gettext_noop("while storing call arguments into local variables");
for (i = 0; i < func->fn_nargs; i++)
{
int n = func->fn_argvarnos[i];
switch (estate.datums[n]->dtype)
{
case PLPGSQL_DTYPE_VAR:
{
PLpgSQL_var *var = (PLpgSQL_var *) estate.datums[n];
assign_simple_var(&estate, var,
fcinfo->args[i].value,
fcinfo->args[i].isnull,
false);
/*
* Force any array-valued parameter to be stored in
* expanded form in our local variable, in hopes of
* improving efficiency of uses of the variable. (This is
* a hack, really: why only arrays? Need more thought
* about which cases are likely to win. See also
* typisarray-specific heuristic in exec_assign_value.)
*
* Special cases: If passed a R/W expanded pointer, assume
* we can commandeer the object rather than having to copy
* it. If passed a R/O expanded pointer, just keep it as
* the value of the variable for the moment. (We'll force
* it to R/W if the variable gets modified, but that may
* very well never happen.)
*/
if (!var->isnull && var->datatype->typisarray)
{
if (VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(var->value)))
{
/* take ownership of R/W object */
assign_simple_var(&estate, var,
TransferExpandedObject(var->value,
estate.datum_context),
false,
true);
}
else if (VARATT_IS_EXTERNAL_EXPANDED_RO(DatumGetPointer(var->value)))
{
/* R/O pointer, keep it as-is until assigned to */
}
else
{
/* flat array, so force to expanded form */
assign_simple_var(&estate, var,
expand_array(var->value,
estate.datum_context,
NULL),
false,
true);
}
}
}
break;
case PLPGSQL_DTYPE_REC:
{
PLpgSQL_rec *rec = (PLpgSQL_rec *) estate.datums[n];
if (!fcinfo->args[i].isnull)
{
/* Assign row value from composite datum */
exec_move_row_from_datum(&estate,
(PLpgSQL_variable *) rec,
fcinfo->args[i].value);
}
else
{
/* If arg is null, set variable to null */
exec_move_row(&estate, (PLpgSQL_variable *) rec,
NULL, NULL);
}
/* clean up after exec_move_row() */
exec_eval_cleanup(&estate);
}
break;
default:
/* Anything else should not be an argument variable */
elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype);
}
}
estate.err_text = gettext_noop("during function entry");
/*
* Set the magic variable FOUND to false
*/
exec_set_found(&estate, false);
/*
* Let the instrumentation plugin peek at this function
*/
if (*plpgsql_plugin_ptr && (*plpgsql_plugin_ptr)->func_beg)
((*plpgsql_plugin_ptr)->func_beg) (&estate, func);
/*
* Now call the toplevel block of statements
*/
estate.err_text = NULL;
rc = exec_toplevel_block(&estate, func->action);
if (rc != PLPGSQL_RC_RETURN)
{
estate.err_text = NULL;
ereport(ERROR,
(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
errmsg("control reached end of function without RETURN")));
}
/*
* We got a return value - process it
*/
estate.err_text = gettext_noop("while casting return value to function's return type");
fcinfo->isnull = estate.retisnull;
if (estate.retisset)
{
ReturnSetInfo *rsi = estate.rsi;
/* Check caller can handle a set result */
if (!rsi || !IsA(rsi, ReturnSetInfo) ||
(rsi->allowedModes & SFRM_Materialize) == 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("set-valued function called in context that cannot accept a set")));
rsi->returnMode = SFRM_Materialize;
/* If we produced any tuples, send back the result */
if (estate.tuple_store)
{
MemoryContext oldcxt;
rsi->setResult = estate.tuple_store;
oldcxt = MemoryContextSwitchTo(estate.tuple_store_cxt);
rsi->setDesc = CreateTupleDescCopy(estate.tuple_store_desc);
MemoryContextSwitchTo(oldcxt);
}
estate.retval = (Datum) 0;
fcinfo->isnull = true;
}
else if (!estate.retisnull)
{
/*
* Cast result value to function's declared result type, and copy it
* out to the upper executor memory context. We must treat tuple
* results specially in order to deal with cases like rowtypes
* involving dropped columns.
*/
if (estate.retistuple)
{
/* Don't need coercion if rowtype is known to match */
if (func->fn_rettype == estate.rettype &&
func->fn_rettype != RECORDOID)
{
/*
* Copy the tuple result into upper executor memory context.
* However, if we have a R/W expanded datum, we can just
* transfer its ownership out to the upper context.
*/
estate.retval = SPI_datumTransfer(estate.retval,
false,
-1);
}
else
{
/*
* Need to look up the expected result type. XXX would be
* better to cache the tupdesc instead of repeating
* get_call_result_type(), but the only easy place to save it
* is in the PLpgSQL_function struct, and that's too
* long-lived: composite types could change during the
* existence of a PLpgSQL_function.
*/
Oid resultTypeId;
TupleDesc tupdesc;
switch (get_call_result_type(fcinfo, &resultTypeId, &tupdesc))
{
case TYPEFUNC_COMPOSITE:
/* got the expected result rowtype, now coerce it */
coerce_function_result_tuple(&estate, tupdesc);
break;
case TYPEFUNC_COMPOSITE_DOMAIN:
/* got the expected result rowtype, now coerce it */
coerce_function_result_tuple(&estate, tupdesc);
/* and check domain constraints */
/* XXX allowing caching here would be good, too */
domain_check(estate.retval, false, resultTypeId,
NULL, NULL);
break;
case TYPEFUNC_RECORD:
/*
* Failed to determine actual type of RECORD. We
* could raise an error here, but what this means in
* practice is that the caller is expecting any old
* generic rowtype, so we don't really need to be
* restrictive. Pass back the generated result as-is.
*/
estate.retval = SPI_datumTransfer(estate.retval,
false,
-1);
break;
default:
/* shouldn't get here if retistuple is true ... */
elog(ERROR, "return type must be a row type");
break;
}
}
}
else
{
/* Scalar case: use exec_cast_value */
estate.retval = exec_cast_value(&estate,
estate.retval,
&fcinfo->isnull,
estate.rettype,
-1,
func->fn_rettype,
-1);
/*
* If the function's return type isn't by value, copy the value
* into upper executor memory context. However, if we have a R/W
* expanded datum, we can just transfer its ownership out to the
* upper executor context.
*/
if (!fcinfo->isnull && !func->fn_retbyval)
estate.retval = SPI_datumTransfer(estate.retval,
false,
func->fn_rettyplen);
}
}
else
{
/*
* We're returning a NULL, which normally requires no conversion work
* regardless of datatypes. But, if we are casting it to a domain
* return type, we'd better check that the domain's constraints pass.
*/
if (func->fn_retisdomain)
estate.retval = exec_cast_value(&estate,
estate.retval,
&fcinfo->isnull,
estate.rettype,
-1,
func->fn_rettype,
-1);
}
estate.err_text = gettext_noop("during function exit");
/*
* Let the instrumentation plugin peek at this function
*/
if (*plpgsql_plugin_ptr && (*plpgsql_plugin_ptr)->func_end)
((*plpgsql_plugin_ptr)->func_end) (&estate, func);
/* Clean up any leftover temporary memory */
plpgsql_destroy_econtext(&estate);
exec_eval_cleanup(&estate);
/* stmt_mcontext will be destroyed when function's main context is */
/*
* Pop the error context stack
*/
error_context_stack = plerrcontext.previous;
/*
* Return the function's result
*/
return estate.retval;
}