diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c index 19e7048002..aa5a45ded4 100644 --- a/src/backend/access/heap/tuptoaster.c +++ b/src/backend/access/heap/tuptoaster.c @@ -1288,6 +1288,74 @@ toast_flatten_tuple_to_datum(HeapTupleHeader tup, } +/* ---------- + * toast_build_flattened_tuple - + * + * Build a tuple containing no out-of-line toasted fields. + * (This does not eliminate compressed or short-header datums.) + * + * This is essentially just like heap_form_tuple, except that it will + * expand any external-data pointers beforehand. + * + * It's not very clear whether it would be preferable to decompress + * in-line compressed datums while at it. For now, we don't. + * ---------- + */ +HeapTuple +toast_build_flattened_tuple(TupleDesc tupleDesc, + Datum *values, + bool *isnull) +{ + HeapTuple new_tuple; + Form_pg_attribute *att = tupleDesc->attrs; + int numAttrs = tupleDesc->natts; + int num_to_free; + int i; + Datum new_values[MaxTupleAttributeNumber]; + Pointer freeable_values[MaxTupleAttributeNumber]; + + /* + * We can pass the caller's isnull array directly to heap_form_tuple, but + * we potentially need to modify the values array. + */ + Assert(numAttrs <= MaxTupleAttributeNumber); + memcpy(new_values, values, numAttrs * sizeof(Datum)); + + num_to_free = 0; + for (i = 0; i < numAttrs; i++) + { + /* + * Look at non-null varlena attributes + */ + if (!isnull[i] && att[i]->attlen == -1) + { + struct varlena *new_value; + + new_value = (struct varlena *) DatumGetPointer(new_values[i]); + if (VARATT_IS_EXTERNAL(new_value)) + { + new_value = heap_tuple_fetch_attr(new_value); + new_values[i] = PointerGetDatum(new_value); + freeable_values[num_to_free++] = (Pointer) new_value; + } + } + } + + /* + * Form the reconfigured tuple. + */ + new_tuple = heap_form_tuple(tupleDesc, new_values, isnull); + + /* + * Free allocated temp values + */ + for (i = 0; i < num_to_free; i++) + pfree(freeable_values[i]); + + return new_tuple; +} + + /* ---------- * toast_compress_datum - * diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 02fb29cde3..982d16c6c8 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -58,7 +58,7 @@ */ #include "postgres.h" -#include "access/htup_details.h" +#include "access/tuptoaster.h" #include "catalog/pg_type.h" #include "executor/execExpr.h" #include "executor/nodeSubplan.h" @@ -3508,24 +3508,24 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext) } /* - * Copy the slot tuple and make sure any toasted fields get detoasted. + * Build a composite datum, making sure any toasted fields get detoasted. * - * (The intermediate copy is a tad annoying here, but we currently have no - * primitive that will do the right thing. Note it is critical that we - * not change the slot's state, so we can't use ExecFetchSlotTupleDatum.) + * (Note: it is critical that we not change the slot's state here.) */ - tuple = ExecCopySlotTuple(slot); - dtuple = (HeapTupleHeader) - DatumGetPointer(heap_copy_tuple_as_datum(tuple, - slot->tts_tupleDescriptor)); - heap_freetuple(tuple); + tuple = toast_build_flattened_tuple(slot->tts_tupleDescriptor, + slot->tts_values, + slot->tts_isnull); + dtuple = tuple->t_data; /* * Label the datum with the composite type info we identified before. + * + * (Note: we could skip doing this by passing op->d.wholerow.tupdesc to + * the tuple build step; but that seems a tad risky so let's not.) */ HeapTupleHeaderSetTypeId(dtuple, op->d.wholerow.tupdesc->tdtypeid); HeapTupleHeaderSetTypMod(dtuple, op->d.wholerow.tupdesc->tdtypmod); - *op->resnull = false; *op->resvalue = PointerGetDatum(dtuple); + *op->resnull = false; } diff --git a/src/include/access/tuptoaster.h b/src/include/access/tuptoaster.h index a6233c386d..c7abeed812 100644 --- a/src/include/access/tuptoaster.h +++ b/src/include/access/tuptoaster.h @@ -193,6 +193,17 @@ extern Datum toast_flatten_tuple_to_datum(HeapTupleHeader tup, uint32 tup_len, TupleDesc tupleDesc); +/* ---------- + * toast_build_flattened_tuple - + * + * Build a tuple containing no out-of-line toasted fields. + * (This does not eliminate compressed or short-header datums.) + * ---------- + */ +extern HeapTuple toast_build_flattened_tuple(TupleDesc tupleDesc, + Datum *values, + bool *isnull); + /* ---------- * toast_compress_datum - *