From 2daf730bda4fcd6f847e64eea6bffd72a893e432 Mon Sep 17 00:00:00 2001 From: Dilip Kumar Date: Thu, 4 Mar 2021 14:55:56 +0530 Subject: [PATCH v38 2/6] Expand the external data before forming the tuple All the callers of HeapTupleGetDatum and HeapTupleHeaderGetDatum, who might contain the external varlena in their tuple, try to flatten the external varlena before forming the tuple wherever it is possible. Dilip Kumar based on idea by Robert Haas --- src/backend/access/common/heaptuple.c | 29 +++++++++++++++++++++++++++++ src/backend/executor/execExprInterp.c | 16 ++++++++-------- src/backend/utils/adt/jsonfuncs.c | 9 +++++++-- src/include/access/htup_details.h | 2 ++ src/pl/plpgsql/src/pl_exec.c | 19 ++++++++++++++----- 5 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index 6061376..c4b5cd7 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -61,6 +61,7 @@ #include "access/sysattr.h" #include "access/tupdesc_details.h" #include "executor/tuptable.h" +#include "fmgr.h" #include "utils/expandeddatum.h" @@ -1114,6 +1115,34 @@ heap_form_tuple(TupleDesc tupleDescriptor, return tuple; } + +/* + * heap_form_flattened_tuple + * wrapper over heap_form_tuple which flatten any external toast pointers + * before forming the tuple. + * + * The result is allocated in the current memory context. + */ +HeapTuple +heap_form_flattened_tuple(TupleDesc tupleDescriptor, + Datum *values, + bool *isnull) +{ + /* detoast any external data before forming the tuple */ + for (int i = 0; i < tupleDescriptor->natts; i++) + { + Form_pg_attribute attr = TupleDescAttr(tupleDescriptor, i); + + if (isnull[i] || attr->attlen != -1 || + !VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + continue; + + values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); + } + + return heap_form_tuple(tupleDescriptor, values, isnull); +} + /* * heap_modify_tuple * form a new tuple from an old tuple and a set of replacement values. diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index e47ab1e..bff6c2b 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2841,11 +2841,11 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) HeapTuple tuple; /* build tuple from evaluated field values */ - tuple = heap_form_tuple(op->d.row.tupdesc, - op->d.row.elemvalues, - op->d.row.elemnulls); + tuple = heap_form_flattened_tuple(op->d.row.tupdesc, + op->d.row.elemvalues, + op->d.row.elemnulls); - *op->resvalue = HeapTupleGetDatum(tuple); + *op->resvalue = HeapTupleGetRawDatum(tuple); *op->resnull = false; } @@ -3086,11 +3086,11 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext HeapTuple tuple; /* argdesc should already be valid from the DeForm step */ - tuple = heap_form_tuple(*op->d.fieldstore.argdesc, - op->d.fieldstore.values, - op->d.fieldstore.nulls); + tuple = heap_form_flattened_tuple(*op->d.fieldstore.argdesc, + op->d.fieldstore.values, + op->d.fieldstore.nulls); - *op->resvalue = HeapTupleGetDatum(tuple); + *op->resvalue = HeapTupleGetRawDatum(tuple); *op->resnull = false; } diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index 5114672..dc3f9d4 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -2968,7 +2968,7 @@ populate_composite(CompositeIOData *io, /* populate resulting record tuple */ tuple = populate_record(io->tupdesc, &io->record_io, defaultval, mcxt, &jso); - result = HeapTupleHeaderGetDatum(tuple); + result = HeapTupleHeaderGetRawDatum(tuple); JsObjectFree(&jso); } @@ -3387,6 +3387,11 @@ populate_record(TupleDesc tupdesc, nulls[i] ? (Datum) 0 : values[i], &field, &nulls[i]); + + /* detoast any external data before forming the tuple */ + if (!nulls[i] && att->attlen == -1 && + VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } res = heap_form_tuple(tupdesc, values, nulls); @@ -3765,7 +3770,7 @@ populate_recordset_record(PopulateRecordsetState *state, JsObject *obj) /* if it's domain over composite, check domain constraints */ if (cache->c.typcat == TYPECAT_COMPOSITE_DOMAIN) - domain_check(HeapTupleHeaderGetDatum(tuphead), false, + domain_check(HeapTupleHeaderGetRawDatum(tuphead), false, cache->argtype, &cache->c.io.composite.domain_info, cache->fn_mcxt); diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h index 7ac3c23..be6641b 100644 --- a/src/include/access/htup_details.h +++ b/src/include/access/htup_details.h @@ -793,6 +793,8 @@ extern Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc); extern Datum heap_copy_tuple_as_raw_datum(HeapTuple tuple, TupleDesc tupleDesc); extern HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull); +extern HeapTuple heap_form_flattened_tuple(TupleDesc tupleDescriptor, + Datum *values, bool *isnull); extern HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index b4c70aa..d4fcb70 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -404,7 +404,8 @@ static void exec_move_row_from_fields(PLpgSQL_execstate *estate, static bool compatible_tupdescs(TupleDesc src_tupdesc, TupleDesc dst_tupdesc); static HeapTuple make_tuple_from_row(PLpgSQL_execstate *estate, PLpgSQL_row *row, - TupleDesc tupdesc); + TupleDesc tupdesc, + bool flatten); static TupleDesc deconstruct_composite_datum(Datum value, HeapTupleData *tmptup); static void exec_move_row_from_datum(PLpgSQL_execstate *estate, @@ -3418,7 +3419,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate, /* Use eval_mcontext for tuple conversion work */ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate)); - tuple = make_tuple_from_row(estate, row, tupdesc); + tuple = make_tuple_from_row(estate, row, tupdesc, false); if (tuple == NULL) /* should not happen */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), @@ -5321,12 +5322,12 @@ exec_eval_datum(PLpgSQL_execstate *estate, /* Make sure we have a valid type/typmod setting */ BlessTupleDesc(row->rowtupdesc); oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate)); - tup = make_tuple_from_row(estate, row, row->rowtupdesc); + tup = make_tuple_from_row(estate, row, row->rowtupdesc, true); if (tup == NULL) /* should not happen */ elog(ERROR, "row not compatible with its own tupdesc"); *typeid = row->rowtupdesc->tdtypeid; *typetypmod = row->rowtupdesc->tdtypmod; - *value = HeapTupleGetDatum(tup); + *value = HeapTupleGetRawDatum(tup); *isnull = false; MemoryContextSwitchTo(oldcontext); break; @@ -7265,12 +7266,15 @@ compatible_tupdescs(TupleDesc src_tupdesc, TupleDesc dst_tupdesc) * * The result tuple is freshly palloc'd in caller's context. Some junk * may be left behind in eval_mcontext, too. + * + * flatten - if this is passed true then any external data will be flattened. * ---------- */ static HeapTuple make_tuple_from_row(PLpgSQL_execstate *estate, PLpgSQL_row *row, - TupleDesc tupdesc) + TupleDesc tupdesc, + bool flatten) { int natts = tupdesc->natts; HeapTuple tuple; @@ -7300,6 +7304,11 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; + + /* detoast any external data if the caller has asked to do so */ + if (flatten && !nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && + VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 1.8.3.1