static void coerce_function_result_tuple()

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;
}