diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index c8fbcf8fcc944ec06d8d745eced4ec4468e9f604..aa8e0e42fc38e448f260dacaed75b14346d7e50d 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -2866,6 +2866,9 @@ batchmemtuples(Tuplesortstate *state) int64 availMemLessRefund; int memtupsize = state->memtupsize; + /* Caller error if we have no tapes */ + Assert(state->activeTapes > 0); + /* For simplicity, assume no memtuples are actually currently counted */ Assert(state->memtupcount == 0); @@ -2879,6 +2882,20 @@ batchmemtuples(Tuplesortstate *state) refund = memtupsize * STANDARDCHUNKHEADERSIZE; availMemLessRefund = state->availMem - refund; + /* + * We need to be sure that we do not cause LACKMEM to become true, else + * the batch allocation size could be calculated as negative, causing + * havoc. Hence, if availMemLessRefund is negative at this point, we must + * do nothing. Moreover, if it's positive but rather small, there's + * little point in proceeding because we could only increase memtuples by + * a small amount, not worth the cost of the repalloc's. We somewhat + * arbitrarily set the threshold at ALLOCSET_DEFAULT_INITSIZE per tape. + * (Note that this does not represent any assumption about tuple sizes.) + */ + if (availMemLessRefund <= + (int64) state->activeTapes * ALLOCSET_DEFAULT_INITSIZE) + return; + /* * To establish balanced memory use after refunding palloc overhead, * temporarily have our accounting indicate that we've allocated all @@ -2888,9 +2905,11 @@ batchmemtuples(Tuplesortstate *state) state->growmemtuples = true; USEMEM(state, availMemLessRefund); (void) grow_memtuples(state); - /* Should not matter, but be tidy */ - FREEMEM(state, availMemLessRefund); state->growmemtuples = false; + /* availMem must stay accurate for spacePerTape calculation */ + FREEMEM(state, availMemLessRefund); + if (LACKMEM(state)) + elog(ERROR, "unexpected out-of-memory situation in tuplesort"); #ifdef TRACE_SORT if (trace_sort)