*** a/src/pl/plpgsql/src/pl_exec.c --- b/src/pl/plpgsql/src/pl_exec.c *************** *** 255,260 **** plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo) --- 255,264 ---- var->value = fcinfo->arg[i]; var->isnull = fcinfo->argnull[i]; var->freeval = false; + + /* only varlena types should be detoasted */ + var->should_be_detoasted = !var->isnull && !var->datatype->typbyval + && var->datatype->typlen == -1; } break; *************** *** 570,581 **** plpgsql_exec_trigger(PLpgSQL_function *func, --- 574,587 ---- elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, UPDATE, or TRUNCATE"); var->isnull = false; var->freeval = true; + var->should_be_detoasted = false; var = (PLpgSQL_var *) (estate.datums[func->tg_name_varno]); var->value = DirectFunctionCall1(namein, CStringGetDatum(trigdata->tg_trigger->tgname)); var->isnull = false; var->freeval = true; + var->should_be_detoasted = false; var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]); if (TRIGGER_FIRED_BEFORE(trigdata->tg_event)) *************** *** 588,593 **** plpgsql_exec_trigger(PLpgSQL_function *func, --- 594,600 ---- elog(ERROR, "unrecognized trigger execution time: not BEFORE, AFTER, or INSTEAD OF"); var->isnull = false; var->freeval = true; + var->should_be_detoasted = false; var = (PLpgSQL_var *) (estate.datums[func->tg_level_varno]); if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) *************** *** 598,620 **** plpgsql_exec_trigger(PLpgSQL_function *func, --- 605,631 ---- elog(ERROR, "unrecognized trigger event type: not ROW or STATEMENT"); var->isnull = false; var->freeval = true; + var->should_be_detoasted = false; var = (PLpgSQL_var *) (estate.datums[func->tg_relid_varno]); var->value = ObjectIdGetDatum(trigdata->tg_relation->rd_id); var->isnull = false; var->freeval = false; + var->should_be_detoasted = false; var = (PLpgSQL_var *) (estate.datums[func->tg_relname_varno]); var->value = DirectFunctionCall1(namein, CStringGetDatum(RelationGetRelationName(trigdata->tg_relation))); var->isnull = false; var->freeval = true; + var->should_be_detoasted = false; var = (PLpgSQL_var *) (estate.datums[func->tg_table_name_varno]); var->value = DirectFunctionCall1(namein, CStringGetDatum(RelationGetRelationName(trigdata->tg_relation))); var->isnull = false; var->freeval = true; + var->should_be_detoasted = false; var = (PLpgSQL_var *) (estate.datums[func->tg_table_schema_varno]); var->value = DirectFunctionCall1(namein, *************** *** 624,634 **** plpgsql_exec_trigger(PLpgSQL_function *func, --- 635,647 ---- trigdata->tg_relation)))); var->isnull = false; var->freeval = true; + var->should_be_detoasted = false; var = (PLpgSQL_var *) (estate.datums[func->tg_nargs_varno]); var->value = Int16GetDatum(trigdata->tg_trigger->tgnargs); var->isnull = false; var->freeval = false; + var->should_be_detoasted = false; var = (PLpgSQL_var *) (estate.datums[func->tg_argv_varno]); if (trigdata->tg_trigger->tgnargs > 0) *************** *** 654,665 **** plpgsql_exec_trigger(PLpgSQL_function *func, --- 667,680 ---- -1, false, 'i')); var->isnull = false; var->freeval = true; + var->should_be_detoasted = false; } else { var->value = (Datum) 0; var->isnull = true; var->freeval = false; + var->should_be_detoasted = false; } estate.err_text = gettext_noop("during function entry"); *************** *** 841,846 **** copy_plpgsql_datum(PLpgSQL_datum *datum) --- 856,862 ---- new->value = 0; new->isnull = true; new->freeval = false; + new->should_be_detoasted = false; result = (PLpgSQL_datum *) new; } *************** *** 2652,2657 **** plpgsql_estate_setup(PLpgSQL_execstate *estate, --- 2668,2679 ---- estate->exitlabel = NULL; estate->cur_error = NULL; + /* + * Store the top execution memory context. exec_eval_datum() explicity + * detoasts varlena datums in this context. + */ + estate->top_exec_cxt = CurrentMemoryContext; + estate->tuple_store = NULL; if (rsi) { *************** *** 3544,3550 **** exec_assign_value(PLpgSQL_execstate *estate, --- 3566,3575 ---- var->value = newvalue; var->isnull = *isNull; if (!var->datatype->typbyval && !*isNull) + { var->freeval = true; + var->should_be_detoasted = var->datatype->typlen == -1; + } break; } *************** *** 3944,3949 **** exec_eval_datum(PLpgSQL_execstate *estate, --- 3969,4002 ---- *typeid = var->datatype->typoid; *typetypmod = var->datatype->atttypmod; + + /* + * explicit deTOAST and decompress for varlena types + */ + if (var->should_be_detoasted) + { + Datum dvalue; + + Assert(!var->isnull); + + /* + * Detoast variables in the top execution context. The + * parser can call this routine (via plpgsql_param_fetch) + * with a different current context. + */ + oldcontext = MemoryContextSwitchTo(estate->top_exec_cxt); + dvalue = PointerGetDatum(PG_DETOAST_DATUM(var->value)); + if (dvalue != var->value) + { + if (var->freeval) + free_var(var); + var->value = dvalue; + var->freeval = true; + } + MemoryContextSwitchTo(oldcontext); + var->should_be_detoasted = false; + } + *value = var->value; *isnull = var->isnull; break; *************** *** 5552,5557 **** assign_text_var(PLpgSQL_var *var, const char *str) --- 5605,5611 ---- var->value = CStringGetTextDatum(str); var->isnull = false; var->freeval = true; + var->should_be_detoasted = false; } /* *** a/src/pl/plpgsql/src/plpgsql.h --- b/src/pl/plpgsql/src/plpgsql.h *************** *** 242,247 **** typedef struct --- 242,248 ---- Datum value; bool isnull; bool freeval; + bool should_be_detoasted; } PLpgSQL_var; *************** *** 643,649 **** typedef struct PLpgSQL_function ItemPointerData fn_tid; bool fn_is_trigger; PLpgSQL_func_hashkey *fn_hashkey; /* back-link to hashtable key */ ! MemoryContext fn_cxt; Oid fn_rettype; int fn_rettyplen; --- 644,650 ---- ItemPointerData fn_tid; bool fn_is_trigger; PLpgSQL_func_hashkey *fn_hashkey; /* back-link to hashtable key */ ! MemoryContext fn_cxt; /* function's persistent context */ Oid fn_rettype; int fn_rettyplen; *************** *** 692,697 **** typedef struct PLpgSQL_execstate --- 693,700 ---- Oid rettype; /* type of current retval */ Oid fn_rettype; /* info about declared function rettype */ + MemoryContext top_exec_cxt; /* function's top execution memory context */ + bool retistuple; bool retisset;