Fix failure to account for memory used by tuplestore_putvalues().

This oversight could result in a tuplestore using much more than the
intended amount of memory.  It would only happen in a code path that loaded
a tuplestore via tuplestore_putvalues(), and many of those won't emit huge
amounts of data; but cases such as holdable cursors and plpgsql's RETURN
NEXT command could have the problem.  The fix ensures that the tuplestore
will switch to write-to-disk mode when it overruns work_mem.

The potential overrun was finite, because we would still count the space
used by the tuple pointer array, so the tuplestore code would eventually
flip into write-to-disk mode anyway.  When storing wide tuples we would
go far past the expected work_mem usage before that happened; but this
may account for the lack of prior reports.

Back-patch to 8.4, where tuplestore_putvalues was introduced.

Per bug #6061 from Yann Delorme.
This commit is contained in:
Tom Lane 2011-06-15 14:05:22 -04:00
parent 6122849416
commit 669ac03af6

View File

@ -570,7 +570,8 @@ tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
MemoryContext oldcxt = MemoryContextSwitchTo(state->context); MemoryContext oldcxt = MemoryContextSwitchTo(state->context);
/* /*
* Copy the tuple. (Must do this even in WRITEFILE case.) * Copy the tuple. (Must do this even in WRITEFILE case. Note that
* COPYTUP includes USEMEM, so we needn't do that here.)
*/ */
tuple = COPYTUP(state, tuple); tuple = COPYTUP(state, tuple);
@ -580,9 +581,8 @@ tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
} }
/* /*
* Similar to tuplestore_puttuple(), but start from the values + nulls * Similar to tuplestore_puttuple(), but work from values + nulls arrays.
* array. This avoids requiring that the caller construct a HeapTuple, * This avoids an extra tuple-construction operation.
* saving a copy.
*/ */
void void
tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc,
@ -592,6 +592,7 @@ tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc,
MemoryContext oldcxt = MemoryContextSwitchTo(state->context); MemoryContext oldcxt = MemoryContextSwitchTo(state->context);
tuple = heap_form_minimal_tuple(tdesc, values, isnull); tuple = heap_form_minimal_tuple(tdesc, values, isnull);
USEMEM(state, GetMemoryChunkSpace(tuple));
tuplestore_puttuple_common(state, (void *) tuple); tuplestore_puttuple_common(state, (void *) tuple);